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 emit_property::PropAndInit;
7 use env::{emitter::Emitter, Env};
8 use error::{Error, Result};
9 use ffi::{Maybe, Maybe::*, Slice, Str};
12 hhas_class::{HhasClass, TraitReqKind},
13 hhas_coeffects::{HhasCoeffects, HhasCtxConstant},
14 hhas_constant::HhasConstant,
15 hhas_method::{HhasMethod, HhasMethodFlags},
16 hhas_param::HhasParam,
18 hhas_property::HhasProperty,
19 hhas_type::{self, HhasTypeInfo},
20 hhas_type_const::HhasTypeConstant,
21 ClassName, FCallArgs, FCallArgsFlags, FatalOp, HhasXhpAttribute, Local, ReadonlyOp,
22 SpecialClsRef, TypedValue, Visibility,
24 use hhbc_string_utils as string_utils;
25 use hhvm_types_ffi::ffi::{Attr, TypeConstraintFlags};
26 use instruction_sequence::{instr, InstrSeq};
27 use itertools::Itertools;
28 use naming_special_names_rust as special_names;
31 ast::{Hint, ReifyKind, RequireKind},
34 use std::collections::BTreeMap;
36 fn add_symbol_refs<'arena, 'decl>(
37 alloc: &'arena bumpalo::Bump,
38 emitter: &mut Emitter<'arena, 'decl>,
39 base: Option<&ClassName<'arena>>,
40 implements: &[ClassName<'arena>],
42 requirements: &[(ClassName<'arena>, TraitReqKind)],
44 base.iter().for_each(|&x| emitter.add_class_ref(*x));
45 implements.iter().for_each(|x| emitter.add_class_ref(*x));
46 uses.iter().for_each(|x| {
47 emitter.add_class_ref(ClassName::from_ast_name_and_mangle(alloc, x.to_owned()))
51 .for_each(|(x, _)| emitter.add_class_ref(*x));
54 fn make_86method<'arena, 'decl>(
55 alloc: &'arena bumpalo::Bump,
56 emitter: &mut Emitter<'arena, 'decl>,
57 name: hhbc::MethodName<'arena>,
58 params: Vec<HhasParam<'arena>>,
60 visibility: Visibility,
63 coeffects: HhasCoeffects<'arena>,
64 instrs: InstrSeq<'arena>,
65 ) -> Result<HhasMethod<'arena>> {
66 // TODO: move this. We just know that there are no iterators in 86methods
67 emitter.iterator_mut().reset();
69 let mut attrs = Attr::AttrNone;
70 attrs.add(Attr::AttrNoInjection);
71 attrs.set(Attr::AttrAbstract, is_abstract);
72 attrs.set(Attr::AttrStatic, is_static);
73 attrs.add(Attr::from(visibility));
75 let attributes = vec![];
76 let flags = HhasMethodFlags::empty();
77 let method_decl_vars = vec![];
78 let method_return_type = None;
79 let method_doc_comment = None;
80 let method_is_memoize_wrapper = false;
81 let method_is_memoize_wrapper_lsb = false;
82 let method_env = None;
84 let body = emit_body::make_body(
89 method_is_memoize_wrapper,
90 method_is_memoize_wrapper_lsb,
93 params.into_iter().map(|p| (p, None)).collect::<Vec<_>>(),
101 attributes: Slice::fill_iter(alloc, attributes.into_iter()),
111 fn from_extends<'arena>(
112 alloc: &'arena bumpalo::Bump,
116 extends: &[ast::Hint],
117 ) -> Option<ClassName<'arena>> {
119 // Do not use special_names:: as there's a prefix \ which breaks HHVM
122 Some(ClassName::from_raw_string(
124 "HH\\BuiltinAbstractEnumClass",
127 Some(ClassName::from_raw_string(alloc, "HH\\BuiltinEnumClass"))
130 Some(ClassName::from_raw_string(alloc, "HH\\BuiltinEnum"))
135 .map(|x| emit_type_hint::hint_to_class(alloc, x))
139 fn from_implements<'arena>(
140 alloc: &'arena bumpalo::Bump,
141 implements: &[ast::Hint],
142 ) -> Vec<ClassName<'arena>> {
145 .map(|x| emit_type_hint::hint_to_class(alloc, x))
149 fn from_includes<'arena>(
150 alloc: &'arena bumpalo::Bump,
151 includes: &[ast::Hint],
152 ) -> Vec<ClassName<'arena>> {
155 .map(|x| emit_type_hint::hint_to_class(alloc, x))
159 fn from_type_constant<'a, 'arena, 'decl>(
160 alloc: &'arena bumpalo::Bump,
161 emitter: &mut Emitter<'arena, 'decl>,
162 tc: &'a ast::ClassTypeconstDef,
163 ) -> Result<HhasTypeConstant<'arena>> {
164 use ast::ClassTypeconst;
165 let name = tc.name.1.to_string();
167 let initializer = match &tc.kind {
168 ClassTypeconst::TCAbstract(ast::ClassAbstractTypeconst { default: None, .. }) => None,
169 ClassTypeconst::TCAbstract(ast::ClassAbstractTypeconst {
173 | ClassTypeconst::TCConcrete(ast::ClassConcreteTypeconst { c_tc_type: init }) => {
174 // TODO: Deal with the constraint
175 // Type constants do not take type vars hence tparams:[]
176 Some(emit_type_constant::hint_to_type_constant(
188 let is_abstract = match &tc.kind {
189 ClassTypeconst::TCConcrete(_) => false,
193 Ok(HhasTypeConstant {
194 name: Str::new_str(alloc, &name),
195 initializer: Maybe::from(initializer),
200 fn from_ctx_constant<'a, 'arena>(
201 alloc: &'arena bumpalo::Bump,
202 tc: &'a ast::ClassTypeconstDef,
203 ) -> Result<HhasCtxConstant<'arena>> {
204 use ast::ClassTypeconst;
205 let name = tc.name.1.to_string();
206 let (recognized, unrecognized) = match &tc.kind {
207 ClassTypeconst::TCAbstract(ast::ClassAbstractTypeconst { default: None, .. }) => {
208 (Slice::empty(), Slice::empty())
210 ClassTypeconst::TCAbstract(ast::ClassAbstractTypeconst {
214 | ClassTypeconst::TCConcrete(ast::ClassConcreteTypeconst { c_tc_type: hint }) => {
215 let x = HhasCoeffects::from_ctx_constant(hint);
216 let r: Slice<'arena, Str<'_>> = Slice::from_vec(
219 .map(|ctx| Str::new_str(alloc, &ctx.to_string()))
222 let u: Slice<'arena, Str<'_>> =
223 Slice::from_vec(alloc, x.1.iter().map(|s| Str::new_str(alloc, s)).collect());
227 let is_abstract = match &tc.kind {
228 ClassTypeconst::TCConcrete(_) => false,
232 name: Str::new_str(alloc, &name),
239 fn from_class_elt_classvars<'a, 'arena, 'decl>(
240 emitter: &mut Emitter<'arena, 'decl>,
241 ast_class: &'a ast::Class_,
242 class_is_const: bool,
245 ) -> Result<Vec<PropAndInit<'arena>>> {
246 // TODO: we need to emit doc comments for each property,
247 // not one per all properties on the same line
248 // The doc comment is only for the first name in the list.
249 // Currently this is organized in the ast_to_nast module
254 let hint = if cv.is_promoted_variadic {
260 emit_property::from_ast(
266 emit_property::FromAstArgs {
267 user_attributes: &cv.user_attributes,
269 initial_value: &cv.expr,
271 // Doc comments are weird. T40098274
272 doc_comment: cv.doc_comment.clone(),
273 visibility: cv.visibility, // This used to be cv_kinds
274 is_static: cv.is_static,
275 is_abstract: cv.abstract_,
276 is_readonly: cv.readonly,
283 fn from_class_elt_constants<'a, 'arena, 'decl>(
284 emitter: &mut Emitter<'arena, 'decl>,
285 env: &Env<'a, 'arena>,
286 class_: &'a ast::Class_,
287 ) -> Result<Vec<(HhasConstant<'arena>, Option<InstrSeq<'arena>>)>> {
288 use oxidized::aast::ClassConstKind;
293 // start unnamed local numbers at 1 for constants to not clobber $constVars
294 emitter.local_gen_mut().reset(Local::new(1));
295 let (is_abstract, init_opt) = match &x.kind {
296 ClassConstKind::CCAbstract(default) => (true, default.as_ref()),
297 ClassConstKind::CCConcrete(expr) => (false, Some(expr)),
299 emit_constant::from_ast(emitter, env, &x.id, is_abstract, init_opt)
304 fn from_class_elt_requirements<'a, 'arena>(
305 alloc: &'arena bumpalo::Bump,
306 class_: &'a ast::Class_,
307 ) -> Vec<(ClassName<'arena>, TraitReqKind)> {
311 .filter_map(|(h, req_kind)| {
312 let class = emit_type_hint::hint_to_class(alloc, h);
314 RequireKind::RequireExtends => Some((class, TraitReqKind::MustExtend)),
315 RequireKind::RequireImplements => Some((class, TraitReqKind::MustImplement)),
316 RequireKind::RequireClass => None,
322 fn from_enum_type<'arena>(
323 alloc: &'arena bumpalo::Bump,
324 opt: Option<&ast::Enum_>,
325 ) -> Result<Option<HhasTypeInfo<'arena>>> {
326 use hhas_type::constraint::Constraint;
328 let type_info_user_type = Just(Str::new_str(
330 &emit_type_hint::fmt_hint(alloc, &[], true, &e.base)?,
332 let type_info_type_constraint =
333 Constraint::make(Nothing, TypeConstraintFlags::ExtendedHint);
334 Ok(HhasTypeInfo::make(
336 type_info_type_constraint,
342 fn validate_class_name(ns: &namespace_env::Env, ast::Id(p, class_name): &ast::Id) -> Result<()> {
343 let is_global_namespace = |ns: &namespace_env::Env| ns.name.is_none();
344 let is_hh_namespace = |ns: &namespace_env::Env| {
347 .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
350 // global names are always reserved in any namespace.
351 // hh_reserved names are checked either if
352 // - class is in global namespace
353 // - class is in HH namespace *)
354 let is_special_class = class_name.contains('$');
355 let check_hh_name = is_global_namespace(ns) || is_hh_namespace(ns);
356 let name = string_utils::strip_ns(class_name);
357 let lower_name = name.to_ascii_lowercase();
358 let is_reserved_global_name = special_names::typehints::is_reserved_global_name(&lower_name);
359 let name_is_reserved = !is_special_class
360 && (is_reserved_global_name
361 || (check_hh_name && special_names::typehints::is_reserved_hh_name(&lower_name)));
362 if name_is_reserved {
363 Err(Error::fatal_parse(
366 "Cannot use '{}' as class name as it is reserved",
367 if is_reserved_global_name {
370 string_utils::strip_global_ns(class_name)
379 fn emit_reified_extends_params<'a, 'arena, 'decl>(
380 e: &mut Emitter<'arena, 'decl>,
381 env: &Env<'a, 'arena>,
382 ast_class: &'a ast::Class_,
383 ) -> Result<InstrSeq<'arena>> {
384 match &ast_class.extends[..] {
385 [h, ..] => match h.1.as_happly() {
386 Some((_, l)) if !l.is_empty() => {
387 return Ok(InstrSeq::gather(vec![
388 emit_expression::emit_reified_targs(
392 &l.iter().collect::<Vec<_>>(),
394 instr::record_reified_generic(),
401 let tv = TypedValue::Vec(Slice::empty());
402 emit_adata::typed_value_to_instr(e, &tv)
405 fn emit_reified_init_body<'a, 'arena, 'decl>(
406 e: &mut Emitter<'arena, 'decl>,
407 env: &Env<'a, 'arena>,
409 ast_class: &'a ast::Class_,
410 init_meth_param_local: Local,
411 ) -> Result<InstrSeq<'arena>> {
412 use string_utils::reified::{INIT_METH_NAME, PROP_NAME};
414 let alloc = env.arena;
415 let check_length = InstrSeq::gather(vec![
416 instr::c_get_l(init_meth_param_local),
417 instr::check_reified_generic_mismatch(),
419 let set_prop = if num_reified == 0 {
422 InstrSeq::gather(vec![
425 instr::c_get_l(init_meth_param_local),
429 hhbc::PropName::from_raw_string(alloc, PROP_NAME),
435 let return_instr = InstrSeq::gather(vec![instr::null(), instr::ret_c()]);
436 Ok(if ast_class.extends.is_empty() {
437 InstrSeq::gather(vec![set_prop, return_instr])
439 let generic_arr = emit_reified_extends_params(e, env, ast_class)?;
440 let call_parent = InstrSeq::gather(vec![
441 instr::null_uninit(),
442 instr::null_uninit(),
444 instr::f_call_cls_method_sd(
446 FCallArgsFlags::default(),
454 SpecialClsRef::ParentCls,
455 hhbc::MethodName::from_raw_string(alloc, INIT_METH_NAME),
459 InstrSeq::gather(vec![set_prop, call_parent, return_instr])
463 fn emit_reified_init_method<'a, 'arena, 'decl>(
464 emitter: &mut Emitter<'arena, 'decl>,
465 env: &Env<'a, 'arena>,
466 ast_class: &'a ast::Class_,
467 ) -> Result<Option<HhasMethod<'arena>>> {
468 use hhas_type::constraint::Constraint;
470 let alloc = env.arena;
471 let num_reified = ast_class
474 .filter(|x| x.reified != ReifyKind::Erased)
476 let maybe_has_reified_parents = match ast_class.extends.first().as_ref() {
477 Some(Hint(_, h)) if h.as_happly().map_or(false, |(_, l)| !l.is_empty()) => true,
478 _ => false, // Hack classes can only extend a single parent
480 if num_reified == 0 && !maybe_has_reified_parents {
483 let tc = Constraint::make(Just("HH\\varray".into()), TypeConstraintFlags::NoFlags);
484 let param_local = Local::new(0);
485 let params = vec![HhasParam {
486 name: Str::new_str(alloc, string_utils::reified::INIT_METH_PARAM_NAME),
490 user_attributes: Slice::empty(),
491 type_info: Just(HhasTypeInfo::make(Just("HH\\varray".into()), tc)),
492 default_value: Nothing,
496 emit_reified_init_body(emitter, env, num_reified, ast_class, param_local)?;
497 let instrs = emit_pos::emit_pos_then(&ast_class.span, body_instrs);
498 Ok(Some(make_86method(
501 hhbc::MethodName::new(Str::new_str(alloc, string_utils::reified::INIT_METH_NAME)),
504 Visibility::Protected,
505 false, // is_abstract
506 HhasSpan::from_pos(&ast_class.span),
507 HhasCoeffects::pure(alloc),
513 fn make_init_method<'a, 'arena, 'decl, F>(
514 alloc: &'arena bumpalo::Bump,
515 emitter: &mut Emitter<'arena, 'decl>,
516 properties: &mut [PropAndInit<'arena>],
520 ) -> Result<Option<HhasMethod<'arena>>>
522 F: Fn(&HhasProperty<'arena>) -> bool,
526 .any(|p| p.init.is_some() && filter(&p.prop))
528 let instrs = InstrSeq::gather(
531 .filter_map(|p| match p.init {
532 Some(_) if filter(&p.prop) => p.init.take(),
533 Some(_) | None => None,
537 let instrs = InstrSeq::gather(vec![instrs, instr::null(), instr::ret_c()]);
538 Ok(Some(make_86method(
541 hhbc::MethodName::new(Str::new_str(alloc, name)),
545 false, // is_abstract
547 HhasCoeffects::pure(alloc),
555 pub fn emit_class<'a, 'arena, 'decl>(
556 alloc: &'arena bumpalo::Bump,
557 emitter: &mut Emitter<'arena, 'decl>,
558 ast_class: &'a ast::Class_,
559 ) -> Result<HhasClass<'arena>> {
560 let namespace = &ast_class.namespace;
561 validate_class_name(namespace, &ast_class.name)?;
562 let mut env = Env::make_class_env(alloc, ast_class);
563 // TODO: communicate this without looking at the name
564 let is_closure = ast_class.name.1.starts_with("Closure$");
566 let mut attributes = emit_attribute::from_asts(emitter, &ast_class.user_attributes)?;
569 .extend(emit_attribute::add_reified_attribute(alloc, &ast_class.tparams).into_iter());
571 emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
575 let is_const = hhas_attribute::has_const(attributes.as_ref());
576 // In the future, we intend to set class_no_dynamic_props independently from
577 // class_is_const, but for now class_is_const is the only thing that turns
579 let no_dynamic_props = is_const;
580 let name = ClassName::from_ast_name_and_mangle(alloc, &ast_class.name.1);
581 let is_trait = ast_class.kind == ast::ClassishKind::Ctrait;
582 let is_interface = ast_class.kind == ast::ClassishKind::Cinterface;
584 let uses: Vec<&str> = ast_class
587 .filter_map(|Hint(pos, hint)| match hint.as_ref() {
588 ast::Hint_::Happly(ast::Id(_, name), _) => {
590 Some(Err(Error::fatal_parse(pos, "Interfaces cannot use traits")))
592 Some(Ok(string_utils::strip_global_ns(name.as_str())))
597 .collect::<Result<Vec<_>>>()?
602 let elaborate_namespace_id =
603 |x: &'a ast::Id| ClassName::from_ast_name_and_mangle(alloc, x.name());
604 let use_aliases = Slice::fill_iter(
609 .map(|ast::UseAsAlias(ido1, id, ido2, vis)| {
610 let id1 = Maybe::from(ido1.as_ref()).map(elaborate_namespace_id);
612 Maybe::from(ido2.as_ref()).map(|x| ClassName::new(Str::new_str(alloc, &x.1)));
615 .fold(Attr::AttrNone, |attr, &v| Attr::from(attr | Attr::from(v)));
617 (id1, ClassName::new(Str::new_str(alloc, &id.1)), id2, attr).into()
621 let use_precedences = Slice::fill_iter(
626 .map(|ast::InsteadofAlias(id1, id2, ids)| {
627 let id1 = elaborate_namespace_id(id1);
628 let id2 = ClassName::new(Str::new_str(alloc, &id2.1));
629 let ids = Slice::fill_iter(alloc, ids.iter().map(elaborate_namespace_id));
630 (id1, id2, ids).into()
634 let enum_type = if ast_class.kind.is_cenum() || ast_class.kind.is_cenum_class() {
635 from_enum_type(alloc, ast_class.enum_.as_ref())?
639 let is_enum_class = ast_class.kind.is_cenum_class();
640 let xhp_attributes: Vec<_> = ast_class
644 |ast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
645 type_: type_.1.as_ref(),
648 maybe_enum: maybe_enum.as_ref(),
653 let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
654 let xhp_categories: Option<(_, Vec<_>)> = ast_class
657 .map(|(p, c)| (p, c.iter().map(|x| &x.1).collect()));
659 let is_abstract = match ast_class.kind {
660 ast::ClassishKind::Cclass(k) => k.is_abstract(),
661 ast::ClassishKind::CenumClass(k) => k.is_abstract(),
664 let is_final = ast_class.final_ || is_trait;
665 let is_sealed = hhas_attribute::has_sealed(attributes.as_ref());
667 let tparams: Vec<&str> = ast_class
670 .map(|x| x.name.1.as_ref())
673 let base = if is_interface {
685 let base_is_closure = || {
686 base.as_ref().map_or(false, |cls| {
687 cls.unsafe_as_str().eq_ignore_ascii_case("closure")
690 if !is_closure && base_is_closure() {
691 return Err(Error::fatal_runtime(
693 "Class cannot extend Closure",
696 let implements = if is_interface {
699 &ast_class.implements
701 let implements = from_implements(alloc, implements);
702 let enum_includes = if ast_class.kind.is_cenum() || ast_class.kind.is_cenum_class() {
703 match &ast_class.enum_ {
705 Some(enum_) => from_includes(alloc, &enum_.includes),
710 let span = HhasSpan::from_pos(&ast_class.span);
711 let mut additional_methods: Vec<HhasMethod<'arena>> = vec![];
712 if let Some(cats) = xhp_categories {
713 additional_methods.push(emit_xhp::from_category_declaration(
714 emitter, ast_class, &cats,
717 if let Some(children) = xhp_children {
718 additional_methods.push(emit_xhp::from_children_declaration(
719 emitter, ast_class, &children,
722 let no_xhp_attributes = xhp_attributes.is_empty() && ast_class.xhp_attr_uses.is_empty();
723 if !no_xhp_attributes {
724 additional_methods.push(emit_xhp::from_attribute_declaration(
728 &ast_class.xhp_attr_uses,
731 emitter.label_gen_mut().reset();
733 from_class_elt_classvars(emitter, ast_class, is_const, &tparams, is_closure)?;
734 let mut constants = from_class_elt_constants(emitter, &env, ast_class)?;
736 let requirements = from_class_elt_requirements(alloc, ast_class);
738 let pinit_filter = |p: &HhasProperty<'_>| !p.flags.is_static();
739 let sinit_filter = |p: &HhasProperty<'_>| p.flags.is_static() && !p.flags.is_lsb();
740 let linit_filter = |p: &HhasProperty<'_>| p.flags.is_static() && p.flags.is_lsb();
742 let pinit_method = make_init_method(
750 let sinit_method = make_init_method(
758 let linit_method = make_init_method(
767 let initialized_constants: Vec<_> = constants
769 .filter_map(|(HhasConstant { ref name, .. }, instrs)| {
772 .map(|instrs| (name, emitter.label_gen_mut().next_regular(), instrs))
775 let cinit_method = if initialized_constants.is_empty() {
778 let param_name = Str::new_str(alloc, "$constName");
779 let param_local = Local::new(0);
780 let params = vec![HhasParam {
785 user_attributes: Slice::empty(),
786 type_info: Nothing, // string?
787 default_value: Nothing,
789 let default_label = emitter.label_gen_mut().next_regular();
791 bumpalo::collections::Vec::with_capacity_in(initialized_constants.len() + 1, alloc);
792 for (name, label, _) in &initialized_constants {
793 let n: &str = alloc.alloc_str((*name).unsafe_as_str());
794 cases.push((n, *label))
796 cases.push((alloc.alloc_str("default"), default_label));
797 let pos = &ast_class.span;
798 let instrs = InstrSeq::gather(vec![
799 emit_pos::emit_pos(pos),
800 instr::c_get_l(param_local),
801 instr::s_switch(alloc, cases),
803 initialized_constants
805 .map(|(_, label, init_instrs)| {
806 // one case for each constant
807 InstrSeq::gather(vec![
810 emit_pos::emit_pos(pos),
816 // default case for constant-not-found
817 instr::label(default_label),
818 emit_pos::emit_pos(pos),
819 instr::string(alloc, "Could not find initializer for "),
820 instr::c_get_l(param_local),
821 instr::string(alloc, " in 86cinit"),
823 instr::fatal(FatalOp::Runtime),
829 hhbc::MethodName::new(Str::new_str(alloc, "86cinit")),
831 true, /* is_static */
833 is_interface, /* is_abstract */
835 HhasCoeffects::default(),
840 let should_emit_reified_init = !(emitter.systemlib() || is_closure || is_interface || is_trait);
841 let reified_init_method = if should_emit_reified_init {
842 emit_reified_init_method(emitter, &env, ast_class)?
846 let needs_no_reifiedinit = reified_init_method.is_some() && ast_class.extends.is_empty();
847 additional_methods.extend(reified_init_method.into_iter());
848 additional_methods.extend(pinit_method.into_iter());
849 additional_methods.extend(sinit_method.into_iter());
850 additional_methods.extend(linit_method.into_iter());
851 additional_methods.extend(cinit_method.into_iter());
853 let mut methods = emit_method::from_asts(emitter, ast_class, &ast_class.methods)?;
854 methods.extend(additional_methods.into_iter());
855 let (ctxconsts, tconsts): (Vec<_>, Vec<_>) =
856 ast_class.typeconsts.iter().partition(|x| x.is_ctx);
857 let type_constants = tconsts
859 .map(|x| from_type_constant(alloc, emitter, x))
860 .collect::<Result<Vec<HhasTypeConstant<'_>>>>()?;
861 let ctx_constants = ctxconsts
863 .map(|x| from_ctx_constant(alloc, x))
864 .collect::<Result<Vec<HhasCtxConstant<'_>>>>()?;
865 let upper_bounds = emit_body::emit_generics_upper_bounds(alloc, &ast_class.tparams, &[], false);
867 if !no_xhp_attributes {
868 properties.push(emit_xhp::properties_for_cache(
869 emitter, ast_class, is_const, is_closure,
872 let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
873 methods.extend(emit_memoize_method::emit_wrapper_methods(
880 let doc_comment = ast_class.doc_comment.clone();
881 let is_closure = methods.iter().any(|x| x.is_closure_body());
882 let is_systemlib = emitter.systemlib();
884 let mut flags = Attr::AttrNone;
885 flags.set(Attr::AttrAbstract, is_abstract);
886 flags.set(Attr::AttrBuiltin, is_systemlib);
887 flags.set(Attr::AttrFinal, is_final);
888 flags.set(Attr::AttrForbidDynamicProps, no_dynamic_props);
889 flags.set(Attr::AttrInterface, is_interface);
890 flags.set(Attr::AttrIsConst, is_const);
891 flags.set(Attr::AttrNoOverride, is_closure);
892 flags.set(Attr::AttrNoReifiedInit, needs_no_reifiedinit);
893 flags.set(Attr::AttrPersistent, is_systemlib);
894 flags.set(Attr::AttrSealed, is_sealed);
895 flags.set(Attr::AttrTrait, is_trait);
896 flags.set(Attr::AttrUnique, is_systemlib);
899 hhas_attribute::has_enum_class(&attributes),
902 Attr::AttrIsFoldable,
903 hhas_attribute::has_foldable(&attributes),
906 Attr::AttrDynamicallyConstructible,
907 hhas_attribute::has_dynamically_constructible(&attributes),
911 enum_type.is_some() && !hhas_attribute::has_enum_class(&attributes),
913 flags.set(Attr::AttrInternal, ast_class.internal);
924 attributes: Slice::fill_iter(alloc, attributes.into_iter()),
925 base: Maybe::from(base),
926 implements: Slice::fill_iter(alloc, implements.into_iter()),
927 enum_includes: Slice::fill_iter(alloc, enum_includes.into_iter()),
931 doc_comment: Maybe::from(doc_comment.map(|c| Str::new_str(alloc, &(c.0).1))),
932 uses: Slice::fill_iter(alloc, uses.into_iter().map(|s| Str::new_str(alloc, s))),
935 methods: Slice::fill_iter(alloc, methods.into_iter()),
936 enum_type: Maybe::from(enum_type),
937 upper_bounds: Slice::fill_iter(alloc, upper_bounds.into_iter()),
938 properties: Slice::fill_iter(alloc, properties.into_iter().map(|p| p.prop)),
939 requirements: Slice::fill_iter(alloc, requirements.into_iter().map(|r| r.into())),
940 type_constants: Slice::fill_iter(alloc, type_constants.into_iter()),
941 ctx_constants: Slice::fill_iter(alloc, ctx_constants.into_iter()),
942 constants: Slice::fill_iter(alloc, constants.into_iter().map(|(c, _)| c)),
946 pub fn emit_classes_from_program<'a, 'arena, 'decl>(
947 alloc: &'arena bumpalo::Bump,
948 emitter: &mut Emitter<'arena, 'decl>,
950 ) -> Result<Vec<HhasClass<'arena>>> {
952 .filter_map(|class| {
953 if let ast::Def::Class(cd) = class {
954 Some(emit_class(alloc, emitter, cd))