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_fatal::raise_fatal_runtime;
7 use env::emitter::Emitter;
8 use ffi::{Maybe::Just, Slice, Str};
9 use hhas_body::HhasBody;
10 use instruction_sequence::{instr, Error::Unrecoverable, InstrSeq, Result};
12 use oxidized::{aast, ast, pos::Pos};
14 pub fn emit_body<'a, 'arena, 'decl>(
15 emitter: &mut Emitter<'arena, 'decl>,
16 scope: &Scope<'a, 'arena>,
17 class_attrs: &[ast::UserAttribute],
19 params: &[ast::FunParam],
20 ret: Option<&aast::Hint>,
21 ) -> Result<HhasBody<'arena>> {
22 let body_instrs = emit_native_opcode_impl(emitter.alloc, &name.1, params, class_attrs);
23 let mut tparams = scope
26 .map(|tp| tp.name.1.as_str())
28 let params = emit_param::from_asts(emitter, &mut tparams, false, scope, params);
29 let return_type_info =
30 emit_body::emit_return_type_info(emitter.alloc, tparams.as_slice(), false, ret);
32 body_instrs.and_then(|body_instrs| {
33 params.and_then(|params| {
34 return_type_info.map(|rti| {
35 let mut body = hhas_body::default_with_body_instrs(body_instrs);
36 body.params = Slice::fill_iter(emitter.alloc, params.into_iter().map(|p| p.0));
37 body.return_type_info = Just(rti);
44 fn emit_native_opcode_impl<'arena>(
45 alloc: &'arena bumpalo::Bump,
47 params: &[ast::FunParam],
48 user_attrs: &[ast::UserAttribute],
49 ) -> Result<InstrSeq<'arena>> {
50 if let [ua] = user_attrs {
51 if ua.name.1 == "__NativeData" {
52 if let [p] = ua.params.as_slice() {
53 match p.2.as_string() {
54 Some(s) if s == "HH\\AsyncGenerator" || s == "Generator" => {
55 return emit_generator_method(alloc, name, params);
62 Err(emit_fatal::raise_fatal_runtime(
64 format!("OpCodeImpl attribute is not applicable to {}", name),
68 fn emit_generator_method<'arena>(
69 alloc: &'arena bumpalo::Bump,
71 params: &[ast::FunParam],
72 ) -> Result<InstrSeq<'arena>> {
73 let instrs = match name {
75 let local = Local::Named(Str::new_str(alloc, get_first_param_name(params)?));
79 instr::contcheck_check(alloc),
80 instr::pushl(alloc, local),
81 instr::contenter(alloc),
85 "raise" | "throw" => {
86 let local = Local::Named(Str::new_str(alloc, get_first_param_name(params)?));
90 instr::contcheck_check(alloc),
91 instr::pushl(alloc, local),
92 instr::contraise(alloc),
96 "next" | "rewind" => InstrSeq::gather(
99 instr::contcheck_ignore(alloc),
101 instr::contenter(alloc),
104 "valid" => instr::contvalid(alloc),
105 "current" => instr::contcurrent(alloc),
106 "key" => instr::contkey(alloc),
107 "getReturn" => instr::contgetreturn(alloc),
109 return Err(raise_fatal_runtime(
111 "incorrect native generator function",
115 Ok(InstrSeq::gather(alloc, vec![instrs, instr::retc(alloc)]))
118 fn get_first_param_name(params: &[ast::FunParam]) -> Result<&str> {
120 [p, ..] => Ok(&p.name),
121 _ => Err(Unrecoverable(String::from(
122 "native generator requires params",