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.
9 use once_cell::sync::OnceCell;
10 use strum::EnumProperty as _;
11 use strum_macros::EnumIter;
12 use strum_macros::EnumProperty;
14 use crate::mangle::MangleWithClass as _;
16 use crate::textual::Sid;
18 const BUILTINS_CLASS: ir::ClassName<'static> = ir::ClassName::new(ffi::Str::new(b"$builtins"));
20 type Result<T = (), E = Error> = std::result::Result<T, E>;
22 /// These represent builtins for handling HHVM bytecode instructions. In general
23 /// the names should match the HHBC name except when they are compound bytecodes
24 /// (like Cmp with a parameter of Eq becoming CmpEq). Documentation can be found
25 /// in hphp/doc/bytecode.specification.
26 #[derive(Copy, Clone, EnumIter, EnumProperty)]
27 pub(crate) enum Hhbc {
28 #[strum(props(Function = "hhbc_add"))]
30 #[strum(props(Function = "hhbc_cmp_eq"))]
32 #[strum(props(Function = "hhbc_cmp_gt"))]
34 #[strum(props(Function = "hhbc_cmp_gte"))]
36 #[strum(props(Function = "hhbc_cmp_lt"))]
38 #[strum(props(Function = "hhbc_cmp_lte"))]
40 #[strum(props(Function = "hhbc_cmp_nsame"))]
42 #[strum(props(Function = "hhbc_cmp_neq"))]
44 #[strum(props(Function = "hhbc_cmp_same"))]
46 #[strum(props(Function = "hhbc_exit"))]
48 #[strum(props(Function = "hhbc_is_type_int"))]
50 #[strum(props(Function = "hhbc_is_type_null"))]
52 #[strum(props(Function = "hhbc_is_type_str"))]
54 #[strum(props(Function = "hhbc_modulo"))]
56 #[strum(props(Function = "hhbc_new_obj"))]
58 #[strum(props(Function = "hhbc_new_vec"))]
60 #[strum(props(Function = "hhbc_not"))]
62 #[strum(props(Function = "hhbc_print"))]
64 #[strum(props(Function = "hhbc_sub"))]
66 #[strum(props(Function = "hhbc_verify_failed"))]
70 // Need Default for EnumIter on Builtin
71 impl std::default::Default for Hhbc {
72 fn default() -> Self {
77 #[derive(EnumIter, EnumProperty)]
78 pub(crate) enum Builtin {
79 /// Allocate an array with the given number of words (a word is a
80 /// pointer-sized value).
81 /// AllocWords(int) -> *void
82 #[strum(props(Function = "alloc_words"))]
84 /// Throws a BadMethodCall exception.
85 /// BadMethodCall() -> noreturn
86 #[strum(props(Function = "hack_bad_method_call"))]
88 /// Throws a BadProperty exception.
89 /// BadProperty() -> noreturn
90 #[strum(props(Function = "hack_bad_property"))]
92 /// Turns a raw boolean into a Mixed.
93 /// Bool(n: bool) -> *Mixed
94 #[strum(props(Function = "hack_bool"))]
96 /// Returns the Class identifier for the given class.
97 #[strum(props(Function = "hack_get_class"))]
99 /// Returns the Class identifier for the given class's static class.
100 #[strum(props(Function = "hack_get_static_class"))]
102 /// Hhbc handlers. See hphp/doc/bytecode.specification for docs.
104 /// Turns a raw int into a Mixed.
105 /// Int(n: int) -> *Mixed
106 #[strum(props(Function = "hack_int"))]
108 /// Returns true if the given Mixed is truthy.
109 /// IsTrue(p: *Mixed) -> bool
110 #[strum(props(Function = "hack_is_true"))]
112 /// Returns a Mixed containing a `null`.
114 #[strum(props(Function = "hack_null"))]
116 /// Returns true if the given raw pointer is null.
117 /// RawPtrIsNull(*void) -> bool
118 #[strum(props(Function = "raw_ptr_is_null"))]
120 /// Turns a raw string into a Mixed.
121 /// String(s: *string) -> *Mixed
122 #[strum(props(Function = "hack_string"))]
124 /// Used to check param count on function entry.
125 /// VerifyParamCount(params, min, max)
126 #[strum(props(Function = "verify_param_count"))]
130 impl fmt::Display for Builtin {
131 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
132 static DUMMY: OnceCell<ir::StringInterner> = OnceCell::new();
133 let strings = DUMMY.get_or_init(Default::default);
135 let name = match self {
136 Builtin::Hhbc(hhbc) => hhbc.get_str("Function").unwrap(),
137 _ => self.get_str("Function").unwrap(),
139 let method = ir::MethodName::new(ffi::Str::new(name.as_bytes()));
140 // Use a dummy string table - this is fine because builtins will never
141 // be based on the UnitBytesId.
142 w.write_str(&method.mangle(&BUILTINS_CLASS, strings))
146 pub(crate) fn call_builtin(
147 w: &mut textual::FuncWriter<'_>,
149 params: impl textual::VarArgs,
151 w.call(&target.to_string(), params)
154 pub(crate) fn expr_builtin(target: Builtin, params: impl textual::VarArgs) -> textual::Expr {
155 textual::Expr::call(target.to_string(), params)