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