don't pass namespace_env to ast_constant_folder
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / emit_class.rs
blob0e94b2c1cfdae6019fe817e9e09abe5ab63ffc9c
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 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;
37 use oxidized::{
38     ast::{self as tast, Hint, ReifyKind, Visibility},
39     namespace_env,
40     pos::Pos,
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>],
50     uses: &[&str],
51     requirements: &[(class::Type<'arena>, TraitReqKind)],
52 ) {
53     base.iter()
54         .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
55     implements
56         .iter()
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))
60     });
61     requirements
62         .iter()
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>>,
71     is_static: bool,
72     visibility: Visibility,
73     is_abstract: bool,
74     span: Span,
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(
96         alloc,
97         emitter,
98         instrs,
99         method_decl_vars,
100         method_is_memoize_wrapper,
101         method_is_memoize_wrapper_lsb,
102         vec![],
103         vec![],
104         params,
105         method_return_type,
106         method_doc_comment,
107         method_env,
108     )?;
110     Ok(HhasMethod {
111         body,
112         attributes,
113         name,
114         flags,
115         span,
116         coeffects,
117         visibility,
118     })
121 fn from_extends<'arena>(
122     alloc: &'arena bumpalo::Bump,
123     is_enum: bool,
124     is_enum_class: bool,
125     extends: &[tast::Hint],
126 ) -> Option<hhbc_id::class::Type<'arena>> {
127     if is_enum {
128         // Do not use special_names:: as there's a prefix \ which breaks HHVM
129         if is_enum_class {
130             Some(hhbc_id::class::from_raw_string(
131                 alloc,
132                 "HH\\BuiltinEnumClass",
133             ))
134         } else {
135             Some(hhbc_id::class::from_raw_string(alloc, "HH\\BuiltinEnum"))
136         }
137     } else {
138         extends
139             .first()
140             .map(|x| emit_type_hint::hint_to_class(alloc, x))
141     }
144 fn from_implements<'arena>(
145     alloc: &'arena bumpalo::Bump,
146     implements: &[tast::Hint],
147 ) -> Vec<hhbc_id::class::Type<'arena>> {
148     implements
149         .iter()
150         .map(|x| emit_type_hint::hint_to_class(alloc, x))
151         .collect()
154 fn from_includes<'arena>(
155     alloc: &'arena bumpalo::Bump,
156     includes: &[tast::Hint],
157 ) -> Vec<hhbc_id::class::Type<'arena>> {
158     includes
159         .iter()
160         .map(|x| emit_type_hint::hint_to_class(alloc, x))
161         .collect()
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(
180                 alloc,
181                 emitter.options(),
182                 &[],
183                 &BTreeMap::new(),
184                 init,
185                 false,
186                 false,
187             )?)
188         }
189     };
191     let is_abstract = match &tc.abstract_ {
192         TCConcrete => false,
193         _ => true,
194     };
196     Ok(HhasTypeConstant {
197         name,
198         initializer,
199         is_abstract,
200     })
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]
211             } else {
212                 result
213             }
214         }
215         (_, None) => vec![],
216     };
217     let is_abstract = match &tc.abstract_ {
218         TCConcrete => false,
219         _ => true,
220     };
221     Ok(HhasCtxConstant {
222         name,
223         coeffects,
224         is_abstract,
225     })
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,
233     tparams: &[&str],
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
239     ast_class
240         .vars
241         .iter()
242         .map(|cv| {
243             let hint = if cv.is_promoted_variadic {
244                 None
245             } else {
246                 cv.type_.1.as_ref()
247             };
249             emit_property::from_ast(
250                 alloc,
251                 emitter,
252                 ast_class,
253                 tparams,
254                 class_is_const,
255                 emit_property::FromAstArgs {
256                     user_attributes: &cv.user_attributes,
257                     id: &cv.id,
258                     initial_value: &cv.expr,
259                     typehint: hint,
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,
266                 },
267             )
268         })
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>>> {
277     class_
278         .consts
279         .iter()
280         .map(|x| hhas_constant::from_ast(emitter, env, &x.id, x.expr.as_ref()))
281         .collect()
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)> {
288     class_
289         .reqs
290         .iter()
291         .map(|(h, is_extends)| {
292             let kind = if *is_extends {
293                 TraitReqKind::MustExtend
294             } else {
295                 TraitReqKind::MustImplement
296             };
297             (emit_type_hint::hint_to_class(alloc, h), kind)
298         })
299         .collect()
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::*;
304     opt.map(|e| {
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(
309             type_info_user_type,
310             type_info_type_constraint,
311         ))
312     })
313     .transpose()
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| {
319         ns.name
320             .as_ref()
321             .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
322     };
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(
338             p,
339             format!(
340                 "Cannot use '{}' as class name as it is reserved",
341                 if is_reserved_global_name {
342                     &name
343                 } else {
344                     string_utils::strip_global_ns(class_name)
345                 }
346             ),
347         ))
348     } else {
349         Ok(())
350     }
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(
363                     alloc,
364                     vec![
365                         emit_expression::emit_reified_targs(
366                             e,
367                             env,
368                             &ast_class.span,
369                             &l.iter().collect::<Vec<_>>(),
370                         )?,
371                         instr::record_reified_generic(alloc),
372                     ],
373                 ));
374             }
375             _ => {}
376         },
377         _ => {}
378     }
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>,
386     num_reified: usize,
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(
393         alloc,
394         vec![
395             instr::cgetl(alloc, hhbc_by_ref_local::Type::Named(INIT_METH_PARAM_NAME)),
396             instr::check_reified_generic_mismatch(alloc),
397         ],
398     );
399     let set_prop = if num_reified == 0 {
400         instr::empty(alloc)
401     } else {
402         InstrSeq::gather(
403             alloc,
404             vec![
405                 check_length,
406                 instr::checkthis(alloc),
407                 instr::cgetl(alloc, hhbc_by_ref_local::Type::Named(INIT_METH_PARAM_NAME)),
408                 instr::baseh(alloc),
409                 instr::setm_pt(
410                     alloc,
411                     0,
412                     prop::from_raw_string(alloc, PROP_NAME),
413                     ReadOnlyOp::Any,
414                 ),
415                 instr::popc(alloc),
416             ],
417         )
418     };
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])
422     } else {
423         let generic_arr = emit_reified_extends_params(e, env, ast_class)?;
424         let call_parent = InstrSeq::gather(
425             alloc,
426             vec![
427                 instr::nulluninit(alloc),
428                 instr::nulluninit(alloc),
429                 generic_arr,
430                 instr::fcallclsmethodsd(
431                     alloc,
432                     FcallArgs::new(alloc, FcallFlags::default(), 1, &[], None, 1, None),
433                     SpecialClsRef::Parent,
434                     method::from_raw_string(alloc, INIT_METH_NAME),
435                 ),
436                 instr::popc(alloc),
437             ],
438         );
439         InstrSeq::gather(alloc, vec![set_prop, call_parent, return_instr])
440     })
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
452         .tparams
453         .iter()
454         .filter(|x| x.reified != ReifyKind::Erased)
455         .count();
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
459     };
460     if num_reified == 0 && !maybe_has_reified_parents {
461         Ok(None)
462     } else {
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(),
466             is_variadic: false,
467             is_inout: false,
468             user_attributes: vec![],
469             type_info: Some(Info::make(Some("HH\\varray".into()), tc)),
470             default_value: None,
471         }];
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(
476             alloc,
477             emitter,
478             (alloc, string_utils::reified::INIT_METH_NAME).into(),
479             params,
480             false, // is_static
481             Visibility::Protected,
482             false, // is_abstract
483             Span::from_pos(&ast_class.span),
484             instrs,
485         )?))
486     }
489 fn make_init_method<'a, 'arena, F>(
490     alloc: &'arena bumpalo::Bump,
491     emitter: &mut Emitter<'arena>,
492     properties: &[HhasProperty<'arena>],
493     filter: F,
494     name: &'static str,
495     span: Span,
496 ) -> Result<Option<HhasMethod<'arena>>>
497 where
498     F: Fn(&HhasProperty<'arena>) -> bool,
500     if properties
501         .iter()
502         .any(|p: &HhasProperty| p.initializer_instrs.is_some() && filter(p))
503     {
504         let instrs = InstrSeq::gather(
505             alloc,
506             properties
507                 .iter()
508                 .filter_map(|p| {
509                     if filter(p) {
510                         // TODO(hrust) this clone can be avoided by wrapping initializer_instrs by Rc
511                         // and also support Rc in InstrSeq
512                         p.initializer_instrs
513                             .as_ref()
514                             .map(|i| InstrSeq::clone(alloc, i))
515                     } else {
516                         None
517                     }
518                 })
519                 .collect(),
520         );
521         let instrs = InstrSeq::gather(alloc, vec![instrs, instr::null(alloc), instr::retc(alloc)]);
522         Ok(Some(make_86method(
523             alloc,
524             emitter,
525             (alloc, name).into(),
526             vec![],
527             true, // is_static
528             Visibility::Private,
529             false, // is_abstract
530             span,
531             instrs,
532         )?))
533     } else {
534         Ok(None)
535     }
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)?;
550     if !is_closure {
551         attributes.extend(emit_attribute::add_reified_attribute(&ast_class.tparams).into_iter());
552         attributes.extend(
553             emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
554         )
555     }
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
560     // it on.
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;
566     let uses = ast_class
567         .uses
568         .iter()
569         .filter_map(|x| match x.1.as_ref() {
570             tast::Hint_::Happly(tast::Id(_, name), _) => {
571                 if is_interface {
572                     Some(Err(emit_fatal::raise_fatal_parse(
573                         &x.0,
574                         "Interfaces cannot use traits",
575                     )))
576                 } else {
577                     Some(Ok(name.as_str()))
578                 }
579             }
580             _ => None,
581         })
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
587         .use_as_alias
588         .iter()
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)
593         })
594         .collect();
596     let use_precedences = ast_class
597         .insteadof_alias
598         .iter()
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();
603             (id1, id2, ids)
604         })
605         .collect();
607     let enum_type = if ast_class.kind == tast::ClassKind::Cenum {
608         from_enum_type(ast_class.enum_.as_ref())?
609     } else {
610         None
611     };
612     let is_enum_class = if ast_class.kind == tast::ClassKind::Cenum {
613         match &ast_class.enum_ {
614             Some(info) => info.enum_class,
615             None => false,
616         }
617     } else {
618         false
619     };
620     let xhp_attributes: Vec<_> = ast_class
621         .xhp_attrs
622         .iter()
623         .map(
624             |tast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
625                 type_: type_.1.as_ref(),
626                 class_var,
627                 tag: *tag,
628                 maybe_enum: maybe_enum.as_ref(),
629             },
630         )
631         .collect();
633     let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
634     let xhp_categories: Option<(_, Vec<_>)> = ast_class
635         .xhp_category
636         .as_ref()
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
644         .tparams
645         .iter()
646         .map(|x| x.name.1.as_ref())
647         .collect();
649     let base = if is_interface {
650         None
651     } else {
652         from_extends(
653             alloc,
654             enum_type.is_some(),
655             is_enum_class,
656             &ast_class.extends,
657         )
658     };
660     if !is_closure
661         && base.as_ref().map_or(false, |cls| {
662             cls.to_raw_string().eq_ignore_ascii_case("closure")
663         })
664     {
665         return Err(emit_fatal::raise_fatal_runtime(
666             &ast_class.name.0,
667             "Class cannot extend Closure",
668         ));
669     }
670     let implements = if is_interface {
671         &ast_class.extends
672     } else {
673         &ast_class.implements
674     };
675     let implements = from_implements(alloc, implements);
676     let enum_includes = if ast_class.kind == tast::ClassKind::Cenum {
677         match &ast_class.enum_ {
678             None => vec![],
679             Some(enum_) => from_includes(alloc, &enum_.includes),
680         }
681     } else {
682         vec![]
683     };
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,
689         )?)
690     }
691     if let Some(children) = xhp_children {
692         additional_methods.push(emit_xhp::from_children_declaration(
693             alloc, emitter, ast_class, &children,
694         )?)
695     }
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(
699             alloc,
700             emitter,
701             ast_class,
702             &xhp_attributes,
703             &ast_class.xhp_attr_uses,
704         )?)
705     }
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();
716     let pinit_method =
717         make_init_method(alloc, emitter, &properties, &pinit_filter, "86pinit", span)?;
718     let sinit_method =
719         make_init_method(alloc, emitter, &properties, &sinit_filter, "86sinit", span)?;
720     let linit_method =
721         make_init_method(alloc, emitter, &properties, &linit_filter, "86linit", span)?;
723     let initialized_constants: Vec<_> = constants
724         .iter()
725         .filter_map(
726             |
727                 HhasConstant {
728                     ref name,
729                     ref initializer_instrs,
730                     ..
731                 },
732             | {
733                 initializer_instrs
734                     .as_ref()
735                     .map(|instrs| (name, emitter.label_gen_mut().next_regular(alloc), instrs))
736             },
737         )
738         .collect();
739     let cinit_method = if initialized_constants.is_empty() {
740         None
741     } else {
742         fn make_cinit_instrs<'arena>(
743             alloc: &'arena bumpalo::Bump,
744             e: &mut Emitter<'arena>,
745             default_label: label::Label<'arena>,
746             pos: &Pos,
747             consts: &[(
748                 &r#const::Type<'arena>,
749                 label::Label<'arena>,
750                 &InstrSeq<'arena>,
751             )],
752         ) -> InstrSeq<'arena> {
753             match consts {
754                 [] => InstrSeq::gather(
755                     alloc,
756                     vec![
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),
764                     ],
765                 ),
766                 [(_, label, instrs), cs @ ..] => InstrSeq::gather(
767                     alloc,
768                     vec![
769                         instr::label(alloc, *label),
770                         InstrSeq::clone(alloc, *instrs),
771                         emit_pos::emit_pos(alloc, pos),
772                         instr::retc(alloc),
773                         make_cinit_instrs(alloc, e, default_label, pos, cs),
774                     ],
775                 ),
776             }
777         }
778         let default_label = emitter.label_gen_mut().next_regular(alloc);
780         let body_instrs = {
781             let mut cases =
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))
786             }
787             cases.push((alloc.alloc_str("default"), default_label));
788             InstrSeq::gather(
789                 alloc,
790                 vec![
791                     instr::cgetl(alloc, hhbc_by_ref_local::Type::Named("$constName")),
792                     instr::sswitch(alloc, cases),
793                     make_cinit_instrs(
794                         alloc,
795                         emitter,
796                         default_label,
797                         &ast_class.span,
798                         &initialized_constants[..],
799                     ),
800                 ],
801             )
802         };
803         let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
804         let params = vec![HhasParam {
805             name: "$constName".to_string(),
806             is_variadic: false,
807             is_inout: false,
808             user_attributes: vec![],
809             type_info: None,
810             default_value: None,
811         }];
813         Some(make_86method(
814             alloc,
815             emitter,
816             (alloc, "86cinit").into(),
817             params,
818             true, /* is_static */
819             Visibility::Private,
820             is_interface, /* is_abstract */
821             span,
822             instrs,
823         )?)
824     };
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)?
829     } else {
830         None
831     };
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
844         .iter()
845         .map(|x| from_type_constant(alloc, emitter, x))
846         .collect::<Result<Vec<HhasTypeConstant>>>()?;
847     let ctx_constants = ctxconsts
848         .iter()
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,
856         )?);
857     }
858     let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
859     methods.extend(emit_memoize_method::emit_wrapper_methods(
860         emitter,
861         &mut env,
862         &info,
863         ast_class,
864         &ast_class.methods,
865     )?);
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);
880     add_symbol_refs(
881         alloc,
882         emitter,
883         &base,
884         &implements,
885         uses.as_ref(),
886         &requirements,
887     );
888     Ok(HhasClass {
889         attributes,
890         base,
891         implements,
892         enum_includes,
893         name,
894         span,
895         flags,
896         doc_comment,
897         uses,
898         use_aliases,
899         use_precedences,
900         methods,
901         enum_type,
902         upper_bounds,
903         properties,
904         requirements,
905         type_constants,
906         ctx_constants,
907         constants,
908     })
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>>> {
916     tast.iter()
917         .filter_map(|class| {
918             if let tast::Def::Class(cd) = class {
919                 Some(emit_class(alloc, emitter, cd))
920             } else {
921                 None
922             }
923         })
924         .collect()