1use crate::compiler::location::LineNumberTable;
2use crate::host::func::HostFunc;
3use crate::object::object::JSObject;
4use crate::runtime::atom::Atom;
5use crate::runtime::context::JSContext;
6use crate::util::FxHashMap;
7use crate::value::JSValue;
8use std::cell::Cell;
9use std::rc::Rc;
10
11const FLAG_IS_ARROW: u8 = 1 << 0;
12const FLAG_IS_ASYNC: u8 = 1 << 1;
13const FLAG_IS_GENERATOR: u8 = 1 << 2;
14const FLAG_IS_BUILTIN: u8 = 1 << 3;
15const FLAG_USES_ARGUMENTS: u8 = 1 << 4;
16const FLAG_IS_STRICT: u8 = 1 << 5;
17
18const FLAG_HAS_SYMBOL_ON_BASE: u8 = 1 << 6;
19
20#[derive(Debug, Clone, Default)]
21pub struct UpvalueData {
22 pub upvalues: FxHashMap<Atom, JSValue>,
23 pub upvalue_cells: FxHashMap<Atom, Rc<Cell<JSValue>>>,
24 pub upvalue_local_indices: FxHashMap<Atom, usize>,
25 pub upvalue_frame_overrides: FxHashMap<Atom, (usize, u64)>,
26
27 pub upvalue_slots: Vec<Rc<Cell<JSValue>>>,
28
29 pub upvalue_slot_atoms: Vec<Atom>,
30
31 pub env_frame: Option<usize>,
32
33 pub env_frame_id: Option<u64>,
34}
35
36impl UpvalueData {
37 pub fn new() -> Self {
38 Self::default()
39 }
40}
41
42#[repr(C)]
43pub struct JSFunction {
44 pub base: JSObject,
45 pub name: Atom,
46 pub param_count: u32,
47 pub locals_count: u32,
48 flags: u8,
49 pub arity: u32,
50
51 pub builtin_atom: Option<Atom>,
52
53 pub builtin_func: Option<HostFunc>,
54 pub upvalues: Option<Box<UpvalueData>>,
55
56 pub bytecode: Option<Box<crate::compiler::opcode::Bytecode>>,
57
58 pub shared_nb_for_ic: Option<std::sync::Arc<crate::compiler::opcode::NestedBytecode>>,
59
60 pub cached_prototype_ptr: *mut crate::object::object::JSObject,
61
62 pub source_filename: String,
63 pub line_number_table: Option<LineNumberTable>,
64}
65
66impl JSFunction {
67 pub fn new() -> Self {
68 JSFunction {
69 base: JSObject::new_function(),
70 name: Atom(0),
71 param_count: 0,
72 locals_count: 256,
73 flags: 0,
74 arity: 0,
75 builtin_atom: None,
76 builtin_func: None,
77 upvalues: None,
78 bytecode: None,
79 shared_nb_for_ic: None,
80 cached_prototype_ptr: std::ptr::null_mut(),
81 source_filename: "<anonymous>".to_string(),
82 line_number_table: None,
83 }
84 }
85
86 pub fn new_builtin(name: Atom, arity: u32) -> Self {
87 let mut f = JSFunction::new();
88 f.name = name;
89 f.flags = FLAG_IS_BUILTIN;
90 f.arity = arity;
91 f
92 }
93
94 pub fn with_upvalues(mut self, upvalues_map: FxHashMap<Atom, JSValue>) -> Self {
95 let mut data = UpvalueData::new();
96 data.upvalues = upvalues_map;
97 self.upvalues = Some(Box::new(data));
98 self
99 }
100
101 pub fn is_callable(&self) -> bool {
102 true
103 }
104
105 #[inline(always)]
106 pub fn is_arrow(&self) -> bool {
107 self.flags & FLAG_IS_ARROW != 0
108 }
109 #[inline(always)]
110 pub fn set_is_arrow(&mut self, val: bool) {
111 if val {
112 self.flags |= FLAG_IS_ARROW;
113 } else {
114 self.flags &= !FLAG_IS_ARROW;
115 }
116 }
117
118 #[inline(always)]
119 pub fn is_async(&self) -> bool {
120 self.flags & FLAG_IS_ASYNC != 0
121 }
122 #[inline(always)]
123 pub fn set_is_async(&mut self, val: bool) {
124 if val {
125 self.flags |= FLAG_IS_ASYNC;
126 } else {
127 self.flags &= !FLAG_IS_ASYNC;
128 }
129 }
130
131 #[inline(always)]
132 pub fn is_generator(&self) -> bool {
133 self.flags & FLAG_IS_GENERATOR != 0
134 }
135 #[inline(always)]
136 pub fn set_is_generator(&mut self, val: bool) {
137 if val {
138 self.flags |= FLAG_IS_GENERATOR;
139 } else {
140 self.flags &= !FLAG_IS_GENERATOR;
141 }
142 }
143
144 #[inline(always)]
145 pub fn is_builtin(&self) -> bool {
146 self.flags & FLAG_IS_BUILTIN != 0
147 }
148 #[inline(always)]
149 pub fn set_is_builtin(&mut self, val: bool) {
150 if val {
151 self.flags |= FLAG_IS_BUILTIN;
152 } else {
153 self.flags &= !FLAG_IS_BUILTIN;
154 }
155 }
156
157 #[inline(always)]
158 pub fn uses_arguments(&self) -> bool {
159 self.flags & FLAG_USES_ARGUMENTS != 0
160 }
161 #[inline(always)]
162 pub fn set_uses_arguments(&mut self, val: bool) {
163 if val {
164 self.flags |= FLAG_USES_ARGUMENTS;
165 } else {
166 self.flags &= !FLAG_USES_ARGUMENTS;
167 }
168 }
169
170 #[inline(always)]
171 pub fn is_strict(&self) -> bool {
172 self.flags & FLAG_IS_STRICT != 0
173 }
174 #[inline(always)]
175 pub fn set_is_strict(&mut self, val: bool) {
176 if val {
177 self.flags |= FLAG_IS_STRICT;
178 } else {
179 self.flags &= !FLAG_IS_STRICT;
180 }
181 }
182
183 #[inline(always)]
184 pub fn has_symbol_on_base(&self) -> bool {
185 self.flags & FLAG_HAS_SYMBOL_ON_BASE != 0
186 }
187
188 #[inline(always)]
189 pub fn mark_has_symbol_prop(&mut self) {
190 self.flags |= FLAG_HAS_SYMBOL_ON_BASE;
191 }
192
193 #[inline]
194 pub fn upvalues_mut(&mut self) -> &mut UpvalueData {
195 if self.upvalues.is_none() {
196 self.upvalues = Some(Box::new(UpvalueData::new()));
197 }
198 self.upvalues.as_mut().unwrap()
199 }
200
201 #[inline]
202 pub fn upvalues_ref(&self) -> Option<&UpvalueData> {
203 self.upvalues.as_deref()
204 }
205
206 pub fn set_builtin_marker(&mut self, ctx: &mut JSContext, builtin_name: &str) {
207 const BUILTIN_MARKER: &str = "__builtin__";
208 let atom = ctx.intern(builtin_name);
209 self.builtin_atom = Some(atom);
210
211 self.builtin_func = ctx.get_builtin_func(builtin_name);
212 self.base
213 .set(ctx.intern(BUILTIN_MARKER), JSValue::new_string(atom));
214 }
215}
216
217impl Default for JSFunction {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_jsfunction_flag_accessors() {
229 let mut f = JSFunction::new();
230 assert!(!f.is_arrow());
231 assert!(!f.is_async());
232 assert!(!f.is_generator());
233 assert!(!f.is_builtin());
234
235 f.set_is_arrow(true);
236 assert!(f.is_arrow());
237
238 f.set_is_async(true);
239 assert!(f.is_async());
240
241 f.set_is_generator(true);
242 assert!(f.is_generator());
243
244 f.set_is_builtin(true);
245 assert!(f.is_builtin());
246
247 f.set_is_arrow(false);
248 assert!(!f.is_arrow());
249
250 assert!(f.is_async());
251 assert!(f.is_generator());
252 assert!(f.is_builtin());
253 }
254
255 #[test]
256 fn test_jsfunction_builtin_constructor() {
257 let f = JSFunction::new_builtin(Atom(42), 3);
258 assert!(f.is_builtin());
259 assert_eq!(f.arity, 3);
260 assert_eq!(f.name, Atom(42));
261 }
262
263 #[test]
264 fn test_upvalue_data_lazily_allocated() {
265 let mut f = JSFunction::new();
266 assert!(f.upvalues.is_none());
267
268 assert!(f.upvalues_ref().is_none());
269 assert!(f.upvalues.is_none());
270
271 f.upvalues_mut()
272 .upvalues
273 .insert(Atom(1), JSValue::new_int(42));
274 assert!(f.upvalues.is_some());
275 assert_eq!(
276 f.upvalues_ref()
277 .unwrap()
278 .upvalues
279 .get(&Atom(1))
280 .unwrap()
281 .get_int(),
282 42
283 );
284 }
285
286 #[test]
287 fn test_upvalue_data_slots_and_atoms() {
288 let mut f = JSFunction::new();
289 let cell = Rc::new(Cell::new(JSValue::new_int(99)));
290 f.upvalues_mut().upvalue_slots.push(cell.clone());
291 f.upvalues_mut().upvalue_slot_atoms.push(Atom(7));
292
293 let uv = f.upvalues_ref().unwrap();
294 assert_eq!(uv.upvalue_slots.len(), 1);
295 assert_eq!(uv.upvalue_slot_atoms[0], Atom(7));
296 assert_eq!(uv.upvalue_slots[0].get().get_int(), 99);
297 }
298
299 #[test]
300 fn test_with_upvalues() {
301 let mut map = FxHashMap::default();
302 map.insert(Atom(1), JSValue::new_int(10));
303 let f = JSFunction::new().with_upvalues(map);
304 assert!(f.upvalues.is_some());
305 assert_eq!(
306 f.upvalues_ref()
307 .unwrap()
308 .upvalues
309 .get(&Atom(1))
310 .unwrap()
311 .get_int(),
312 10
313 );
314 }
315}