minor refactoring
[hiphop-php.git] / hphp / hack / src / hackc / external_decl_provider / lib.rs
blob1306a1c7e5ccc397a907effaa487e9160a2414eb
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 arena_deserializer::serde::Deserialize;
7 use decl_provider::{self, DeclProvider};
8 use libc::{c_char, c_int};
9 use oxidized_by_ref::file_info::NameType;
10 use oxidized_by_ref::{direct_decl_parser, shallow_decl_defs::Decl};
13 Technically, the object layout of the values we are working with
14 (produced in C++) is this (c.f. 'rust_ffi_compile.rs' and
15 'hh_single_compile.cpp' (for example)):
16 ```rust
17   struct Decls<'decl>(direct_decl_parser::Decls<'decl>);
18   struct Bytes(ffi::Bytes);
20   enum ExternalDeclProviderResult<'decl> {
21       Missing,
22       Decls(*const Decls<'decl>),
23       Bytes(*const Bytes),
24   }
25 ```
26 That is, when C++ returns us results, this is formally the layout we
27 can expect in Rust.
29 With that definition for `ExternalDeclProvider<'decl>` then, in the
30 `ExternalDeclProviderResult::Decls(p)` case you can get at the actual
31 decls as:
32 ```
33   let decls: &Decls<'decl> = &(unsafe { p.as_ref() }.unwrap().0);
34 ```
36 This definition for `ExternalDeclProvider<'decl>` and that access
37 style naturally works. There is a simplification we can make though.
39 We can treat:
40     - a `*const Decls<'decl>` as a `*const direct_decl_parser::Decls<'decl>`
41     - a `*const Bytes` as a `*const ffi::Bytes`
42 (both points follow from the fact that the address of a POD is equal
43 to the address of its first member).
45 Therefore, we arrive at the equivalent but more direct
46 ```
47   pub enum ExternalDeclProviderResult<'decl> {
48       Missing,
49       Decls(*const direct_decls::Decls<'decl>),
50       Bytes(*const ffi::Bytes),
51   }
52 ```
53 and, given a value of case `ExternalDeclProviderResult::Decls(p)`, go
54 straight to the decls with:
55 ```
56 let decls: &Decls<'decl> = unsafe { p.as_ref() }.unwrap();
57 ```
59 #[repr(C)]
60 pub enum ExternalDeclProviderResult<'decl> {
61     Missing,
62     Decls(*const direct_decl_parser::Decls<'decl>),
63     Bytes(*const ffi::Bytes),
66 #[derive(Debug)]
67 pub struct ExternalDeclProvider<'decl>(
68     pub  unsafe extern "C" fn(
69         *const std::ffi::c_void, // Caller provided cookie
70         c_int,                   // A proxy for `HPHP::AutoloadMap::KindOf`
71         *const c_char,           // The symbol
72     ) -> ExternalDeclProviderResult<'decl>, // Possible payload: `*const Decl<'decl>` or, `const* Bytes`
73     pub *const std::ffi::c_void,
74     &'decl bumpalo::Bump,
77 impl<'decl> DeclProvider<'decl> for ExternalDeclProvider<'decl> {
78     fn get_decl(&self, kind: NameType, symbol: &str) -> Result<Decl<'decl>, decl_provider::Error> {
79         // Need to convert NameType into HPHP::AutoloadMap::KindOf.
80         let code: i32 = match kind {
81             NameType::Class => 0,   // HPHP::AutoloadMap::KindOf::Type
82             NameType::Typedef => 3, // HPHP::AutoloadMap::KindOf::TypeAlias
83             NameType::Fun => 1,     // HPHP::AutoloadMap::KindOf::Function
84             NameType::Const => 2,   // HPHP::AutoloadMap::KindOf::Constant
85         };
86         let symbol_ptr = std::ffi::CString::new(symbol).unwrap();
87         match unsafe { self.0(self.1, code, symbol_ptr.as_c_str().as_ptr()) } {
88             ExternalDeclProviderResult::Missing => Err(decl_provider::Error::NotFound),
89             ExternalDeclProviderResult::Decls(p) => {
90                 let decls: &direct_decl_parser::Decls<'decl> = unsafe { p.as_ref() }.unwrap();
91                 let decl = decls
92                     .iter()
93                     .find_map(|(sym, decl)| if sym == symbol { Some(decl) } else { None });
94                 match decl {
95                     None => Err(decl_provider::Error::NotFound),
96                     Some(decl) => Ok(decl),
97                 }
98             }
99             ExternalDeclProviderResult::Bytes(p) => {
100                 let bytes: &ffi::Bytes = unsafe { p.as_ref() }.unwrap();
101                 let arena = self.2;
102                 let data = unsafe { std::slice::from_raw_parts(bytes.data, bytes.len) };
103                 let op = bincode::config::Options::with_native_endian(bincode::options());
104                 let mut de = bincode::de::Deserializer::from_slice(data, op);
106                 let de = arena_deserializer::ArenaDeserializer::new(arena, &mut de);
107                 let decls = direct_decl_parser::Decls::deserialize(de)
108                     .map_err(|e| format!("failed to deserialize, error: {}", e))
109                     .unwrap();
111                 let decl = decls
112                     .iter()
113                     .find_map(|(sym, decl)| if sym == symbol { Some(decl) } else { None });
114                 match decl {
115                     None => Err(decl_provider::Error::NotFound),
116                     Some(decl) => Ok(decl),
117                 }
118             }
119         }
120     }
121 } //impl<'decl> DeclProvider<'decl> for ExternalDeclProvider<'decl>
123 impl<'decl> ExternalDeclProvider<'decl> {
124     pub fn new(
125         decl_getter: unsafe extern "C" fn(
126             *const std::ffi::c_void,
127             c_int,
128             *const c_char,
129         ) -> ExternalDeclProviderResult<'decl>,
130         decl_provider: *const std::ffi::c_void,
131         decl_allocator: &'decl bumpalo::Bump,
132     ) -> Self {
133         Self(decl_getter, decl_provider, decl_allocator)
134     }