Add internal identifier to functions, methods, properties and classes
[hiphop-php.git] / hphp / hack / src / hackc / emitter / emit_class.rs
blobe93ee0958b311a6ddf05738ba160e8bda3ded522
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
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};
10 use hhbc::{
11     hhas_attribute,
12     hhas_class::{HhasClass, TraitReqKind},
13     hhas_coeffects::{HhasCoeffects, HhasCtxConstant},
14     hhas_constant::HhasConstant,
15     hhas_method::{HhasMethod, HhasMethodFlags},
16     hhas_param::HhasParam,
17     hhas_pos::HhasSpan,
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;
29 use oxidized::{
30     ast,
31     ast::{Hint, ReifyKind, RequireKind},
32     namespace_env,
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>],
41     uses: &[&str],
42     requirements: &[(ClassName<'arena>, TraitReqKind)],
43 ) {
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()))
48     });
49     requirements
50         .iter()
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>>,
59     is_static: bool,
60     visibility: Visibility,
61     is_abstract: bool,
62     span: HhasSpan,
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(
85         alloc,
86         emitter,
87         instrs,
88         method_decl_vars,
89         method_is_memoize_wrapper,
90         method_is_memoize_wrapper_lsb,
91         vec![],
92         vec![],
93         params.into_iter().map(|p| (p, None)).collect::<Vec<_>>(),
94         method_return_type,
95         method_doc_comment,
96         method_env,
97     )?;
99     Ok(HhasMethod {
100         body,
101         attributes: Slice::fill_iter(alloc, attributes.into_iter()),
102         name,
103         flags,
104         span,
105         coeffects,
106         visibility,
107         attrs,
108     })
111 fn from_extends<'arena>(
112     alloc: &'arena bumpalo::Bump,
113     is_enum: bool,
114     is_enum_class: bool,
115     is_abstract: bool,
116     extends: &[ast::Hint],
117 ) -> Option<ClassName<'arena>> {
118     if is_enum {
119         // Do not use special_names:: as there's a prefix \ which breaks HHVM
120         if is_enum_class {
121             if is_abstract {
122                 Some(ClassName::from_raw_string(
123                     alloc,
124                     "HH\\BuiltinAbstractEnumClass",
125                 ))
126             } else {
127                 Some(ClassName::from_raw_string(alloc, "HH\\BuiltinEnumClass"))
128             }
129         } else {
130             Some(ClassName::from_raw_string(alloc, "HH\\BuiltinEnum"))
131         }
132     } else {
133         extends
134             .first()
135             .map(|x| emit_type_hint::hint_to_class(alloc, x))
136     }
139 fn from_implements<'arena>(
140     alloc: &'arena bumpalo::Bump,
141     implements: &[ast::Hint],
142 ) -> Vec<ClassName<'arena>> {
143     implements
144         .iter()
145         .map(|x| emit_type_hint::hint_to_class(alloc, x))
146         .collect()
149 fn from_includes<'arena>(
150     alloc: &'arena bumpalo::Bump,
151     includes: &[ast::Hint],
152 ) -> Vec<ClassName<'arena>> {
153     includes
154         .iter()
155         .map(|x| emit_type_hint::hint_to_class(alloc, x))
156         .collect()
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 {
170             default: Some(init),
171             ..
172         })
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(
177                 alloc,
178                 emitter.options(),
179                 &[],
180                 &BTreeMap::new(),
181                 init,
182                 false,
183                 false,
184             )?)
185         }
186     };
188     let is_abstract = match &tc.kind {
189         ClassTypeconst::TCConcrete(_) => false,
190         _ => true,
191     };
193     Ok(HhasTypeConstant {
194         name: Str::new_str(alloc, &name),
195         initializer: Maybe::from(initializer),
196         is_abstract,
197     })
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())
209         }
210         ClassTypeconst::TCAbstract(ast::ClassAbstractTypeconst {
211             default: Some(hint),
212             ..
213         })
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(
217                 alloc,
218                 x.0.iter()
219                     .map(|ctx| Str::new_str(alloc, &ctx.to_string()))
220                     .collect(),
221             );
222             let u: Slice<'arena, Str<'_>> =
223                 Slice::from_vec(alloc, x.1.iter().map(|s| Str::new_str(alloc, s)).collect());
224             (r, u)
225         }
226     };
227     let is_abstract = match &tc.kind {
228         ClassTypeconst::TCConcrete(_) => false,
229         _ => true,
230     };
231     Ok(HhasCtxConstant {
232         name: Str::new_str(alloc, &name),
233         recognized,
234         unrecognized,
235         is_abstract,
236     })
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,
243     tparams: &[&str],
244     is_closure: 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
250     ast_class
251         .vars
252         .iter()
253         .map(|cv| {
254             let hint = if cv.is_promoted_variadic {
255                 None
256             } else {
257                 cv.type_.1.as_ref()
258             };
260             emit_property::from_ast(
261                 emitter,
262                 ast_class,
263                 tparams,
264                 class_is_const,
265                 is_closure,
266                 emit_property::FromAstArgs {
267                     user_attributes: &cv.user_attributes,
268                     id: &cv.id,
269                     initial_value: &cv.expr,
270                     typehint: hint,
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,
277                 },
278             )
279         })
280         .collect()
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;
289     class_
290         .consts
291         .iter()
292         .map(|x| {
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)),
298             };
299             emit_constant::from_ast(emitter, env, &x.id, is_abstract, init_opt)
300         })
301         .collect()
304 fn from_class_elt_requirements<'a, 'arena>(
305     alloc: &'arena bumpalo::Bump,
306     class_: &'a ast::Class_,
307 ) -> Vec<(ClassName<'arena>, TraitReqKind)> {
308     class_
309         .reqs
310         .iter()
311         .filter_map(|(h, req_kind)| {
312             let class = emit_type_hint::hint_to_class(alloc, h);
313             match *req_kind {
314                 RequireKind::RequireExtends => Some((class, TraitReqKind::MustExtend)),
315                 RequireKind::RequireImplements => Some((class, TraitReqKind::MustImplement)),
316                 RequireKind::RequireClass => None,
317             }
318         })
319         .collect()
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;
327     opt.map(|e| {
328         let type_info_user_type = Just(Str::new_str(
329             alloc,
330             &emit_type_hint::fmt_hint(alloc, &[], true, &e.base)?,
331         ));
332         let type_info_type_constraint =
333             Constraint::make(Nothing, TypeConstraintFlags::ExtendedHint);
334         Ok(HhasTypeInfo::make(
335             type_info_user_type,
336             type_info_type_constraint,
337         ))
338     })
339     .transpose()
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| {
345         ns.name
346             .as_ref()
347             .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
348     };
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(
364             p,
365             format!(
366                 "Cannot use '{}' as class name as it is reserved",
367                 if is_reserved_global_name {
368                     &name
369                 } else {
370                     string_utils::strip_global_ns(class_name)
371                 }
372             ),
373         ))
374     } else {
375         Ok(())
376     }
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(
389                         e,
390                         env,
391                         &ast_class.span,
392                         &l.iter().collect::<Vec<_>>(),
393                     )?,
394                     instr::record_reified_generic(),
395                 ]));
396             }
397             _ => {}
398         },
399         _ => {}
400     }
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>,
408     num_reified: usize,
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(),
418     ]);
419     let set_prop = if num_reified == 0 {
420         instr::empty()
421     } else {
422         InstrSeq::gather(vec![
423             check_length,
424             instr::check_this(),
425             instr::c_get_l(init_meth_param_local),
426             instr::base_h(),
427             instr::set_m_pt(
428                 0,
429                 hhbc::PropName::from_raw_string(alloc, PROP_NAME),
430                 ReadonlyOp::Any,
431             ),
432             instr::pop_c(),
433         ])
434     };
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])
438     } else {
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(),
443             generic_arr,
444             instr::f_call_cls_method_sd(
445                 FCallArgs::new(
446                     FCallArgsFlags::default(),
447                     1,
448                     1,
449                     Slice::empty(),
450                     Slice::empty(),
451                     None,
452                     None,
453                 ),
454                 SpecialClsRef::ParentCls,
455                 hhbc::MethodName::from_raw_string(alloc, INIT_METH_NAME),
456             ),
457             instr::pop_c(),
458         ]);
459         InstrSeq::gather(vec![set_prop, call_parent, return_instr])
460     })
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
472         .tparams
473         .iter()
474         .filter(|x| x.reified != ReifyKind::Erased)
475         .count();
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
479     };
480     if num_reified == 0 && !maybe_has_reified_parents {
481         Ok(None)
482     } else {
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),
487             is_variadic: false,
488             is_inout: false,
489             is_readonly: false,
490             user_attributes: Slice::empty(),
491             type_info: Just(HhasTypeInfo::make(Just("HH\\varray".into()), tc)),
492             default_value: Nothing,
493         }];
495         let body_instrs =
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(
499             alloc,
500             emitter,
501             hhbc::MethodName::new(Str::new_str(alloc, string_utils::reified::INIT_METH_NAME)),
502             params,
503             false, // is_static
504             Visibility::Protected,
505             false, // is_abstract
506             HhasSpan::from_pos(&ast_class.span),
507             HhasCoeffects::pure(alloc),
508             instrs,
509         )?))
510     }
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>],
517     filter: F,
518     name: &'static str,
519     span: HhasSpan,
520 ) -> Result<Option<HhasMethod<'arena>>>
521 where
522     F: Fn(&HhasProperty<'arena>) -> bool,
524     if properties
525         .iter()
526         .any(|p| p.init.is_some() && filter(&p.prop))
527     {
528         let instrs = InstrSeq::gather(
529             properties
530                 .iter_mut()
531                 .filter_map(|p| match p.init {
532                     Some(_) if filter(&p.prop) => p.init.take(),
533                     Some(_) | None => None,
534                 })
535                 .collect(),
536         );
537         let instrs = InstrSeq::gather(vec![instrs, instr::null(), instr::ret_c()]);
538         Ok(Some(make_86method(
539             alloc,
540             emitter,
541             hhbc::MethodName::new(Str::new_str(alloc, name)),
542             vec![],
543             true, // is_static
544             Visibility::Private,
545             false, // is_abstract
546             span,
547             HhasCoeffects::pure(alloc),
548             instrs,
549         )?))
550     } else {
551         Ok(None)
552     }
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)?;
567     if !is_closure {
568         attributes
569             .extend(emit_attribute::add_reified_attribute(alloc, &ast_class.tparams).into_iter());
570         attributes.extend(
571             emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
572         )
573     }
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
578     // it on.
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
585         .uses
586         .iter()
587         .filter_map(|Hint(pos, hint)| match hint.as_ref() {
588             ast::Hint_::Happly(ast::Id(_, name), _) => {
589                 if is_interface {
590                     Some(Err(Error::fatal_parse(pos, "Interfaces cannot use traits")))
591                 } else {
592                     Some(Ok(string_utils::strip_global_ns(name.as_str())))
593                 }
594             }
595             _ => None,
596         })
597         .collect::<Result<Vec<_>>>()?
598         .into_iter()
599         .unique()
600         .collect();
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(
605         alloc,
606         ast_class
607             .use_as_alias
608             .iter()
609             .map(|ast::UseAsAlias(ido1, id, ido2, vis)| {
610                 let id1 = Maybe::from(ido1.as_ref()).map(elaborate_namespace_id);
611                 let id2 =
612                     Maybe::from(ido2.as_ref()).map(|x| ClassName::new(Str::new_str(alloc, &x.1)));
613                 let attr = vis
614                     .iter()
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()
618             }),
619     );
621     let use_precedences = Slice::fill_iter(
622         alloc,
623         ast_class
624             .insteadof_alias
625             .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()
631             }),
632     );
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())?
636     } else {
637         None
638     };
639     let is_enum_class = ast_class.kind.is_cenum_class();
640     let xhp_attributes: Vec<_> = ast_class
641         .xhp_attrs
642         .iter()
643         .map(
644             |ast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
645                 type_: type_.1.as_ref(),
646                 class_var,
647                 tag: *tag,
648                 maybe_enum: maybe_enum.as_ref(),
649             },
650         )
651         .collect();
653     let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
654     let xhp_categories: Option<(_, Vec<_>)> = ast_class
655         .xhp_category
656         .as_ref()
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(),
662         _ => false,
663     };
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
668         .tparams
669         .iter()
670         .map(|x| x.name.1.as_ref())
671         .collect();
673     let base = if is_interface {
674         None
675     } else {
676         from_extends(
677             alloc,
678             enum_type.is_some(),
679             is_enum_class,
680             is_abstract,
681             &ast_class.extends,
682         )
683     };
685     let base_is_closure = || {
686         base.as_ref().map_or(false, |cls| {
687             cls.unsafe_as_str().eq_ignore_ascii_case("closure")
688         })
689     };
690     if !is_closure && base_is_closure() {
691         return Err(Error::fatal_runtime(
692             &ast_class.name.0,
693             "Class cannot extend Closure",
694         ));
695     }
696     let implements = if is_interface {
697         &ast_class.extends
698     } else {
699         &ast_class.implements
700     };
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_ {
704             None => vec![],
705             Some(enum_) => from_includes(alloc, &enum_.includes),
706         }
707     } else {
708         vec![]
709     };
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,
715         )?)
716     }
717     if let Some(children) = xhp_children {
718         additional_methods.push(emit_xhp::from_children_declaration(
719             emitter, ast_class, &children,
720         )?)
721     }
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(
725             emitter,
726             ast_class,
727             &xhp_attributes,
728             &ast_class.xhp_attr_uses,
729         )?)
730     }
731     emitter.label_gen_mut().reset();
732     let mut properties =
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(
743         alloc,
744         emitter,
745         &mut properties,
746         &pinit_filter,
747         "86pinit",
748         span,
749     )?;
750     let sinit_method = make_init_method(
751         alloc,
752         emitter,
753         &mut properties,
754         &sinit_filter,
755         "86sinit",
756         span,
757     )?;
758     let linit_method = make_init_method(
759         alloc,
760         emitter,
761         &mut properties,
762         &linit_filter,
763         "86linit",
764         span,
765     )?;
767     let initialized_constants: Vec<_> = constants
768         .iter_mut()
769         .filter_map(|(HhasConstant { ref name, .. }, instrs)| {
770             instrs
771                 .take()
772                 .map(|instrs| (name, emitter.label_gen_mut().next_regular(), instrs))
773         })
774         .collect();
775     let cinit_method = if initialized_constants.is_empty() {
776         None
777     } else {
778         let param_name = Str::new_str(alloc, "$constName");
779         let param_local = Local::new(0);
780         let params = vec![HhasParam {
781             name: param_name,
782             is_variadic: false,
783             is_inout: false,
784             is_readonly: false,
785             user_attributes: Slice::empty(),
786             type_info: Nothing, // string?
787             default_value: Nothing,
788         }];
789         let default_label = emitter.label_gen_mut().next_regular();
790         let mut cases =
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))
795         }
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),
802             InstrSeq::gather(
803                 initialized_constants
804                     .into_iter()
805                     .map(|(_, label, init_instrs)| {
806                         // one case for each constant
807                         InstrSeq::gather(vec![
808                             instr::label(label),
809                             init_instrs,
810                             emit_pos::emit_pos(pos),
811                             instr::ret_c(),
812                         ])
813                     })
814                     .collect(),
815             ),
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"),
822             instr::concat_n(3),
823             instr::fatal(FatalOp::Runtime),
824         ]);
826         Some(make_86method(
827             alloc,
828             emitter,
829             hhbc::MethodName::new(Str::new_str(alloc, "86cinit")),
830             params,
831             true, /* is_static */
832             Visibility::Private,
833             is_interface, /* is_abstract */
834             span,
835             HhasCoeffects::default(),
836             instrs,
837         )?)
838     };
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)?
843     } else {
844         None
845     };
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
858         .iter()
859         .map(|x| from_type_constant(alloc, emitter, x))
860         .collect::<Result<Vec<HhasTypeConstant<'_>>>>()?;
861     let ctx_constants = ctxconsts
862         .iter()
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,
870         )?);
871     }
872     let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
873     methods.extend(emit_memoize_method::emit_wrapper_methods(
874         emitter,
875         &mut env,
876         &info,
877         ast_class,
878         &ast_class.methods,
879     )?);
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);
897     flags.set(
898         Attr::AttrEnumClass,
899         hhas_attribute::has_enum_class(&attributes),
900     );
901     flags.set(
902         Attr::AttrIsFoldable,
903         hhas_attribute::has_foldable(&attributes),
904     );
905     flags.set(
906         Attr::AttrDynamicallyConstructible,
907         hhas_attribute::has_dynamically_constructible(&attributes),
908     );
909     flags.set(
910         Attr::AttrEnum,
911         enum_type.is_some() && !hhas_attribute::has_enum_class(&attributes),
912     );
913     flags.set(Attr::AttrInternal, ast_class.internal);
915     add_symbol_refs(
916         alloc,
917         emitter,
918         base.as_ref(),
919         &implements,
920         uses.as_ref(),
921         &requirements,
922     );
923     Ok(HhasClass {
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()),
928         name,
929         span,
930         flags,
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))),
933         use_aliases,
934         use_precedences,
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)),
943     })
946 pub fn emit_classes_from_program<'a, 'arena, 'decl>(
947     alloc: &'arena bumpalo::Bump,
948     emitter: &mut Emitter<'arena, 'decl>,
949     ast: &'a [ast::Def],
950 ) -> Result<Vec<HhasClass<'arena>>> {
951     ast.iter()
952         .filter_map(|class| {
953             if let ast::Def::Class(cd) = class {
954                 Some(emit_class(alloc, emitter, cd))
955             } else {
956                 None
957             }
958         })
959         .collect()