1use gc_lite::{GcRef, GcTracable};
2use smallvec::SmallVec;
3
4use crate::{
5 JS_CLASS_ASYNC_FUNCTION, JS_CLASS_ASYNC_GENERATOR, JS_CLASS_ASYNC_GENERATOR_FUNCTION,
6 JS_CLASS_BYTECODE_FUNCTION, JS_CLASS_GENERATOR, JS_CLASS_GENERATOR_FUNCTION,
7 atom::Atom,
8 class::ClassID,
9 context::{ContextImpl, ContextPtr},
10 error::JSResult,
11 eval::{EvalFlags, EvalType},
12 function::{Bytecode, CallContext, JS_CALL_FLAG_COPY_ARGV, JSVarRef},
13 object::{
14 JS_PROP_CONFIGURABLE, JS_PROP_NORMAL, JS_PROP_TMASK, JSProperty, Object, ObjectImpl,
15 ObjectPayload,
16 },
17 object_cast,
18 runtime::js_check_stack_overflow,
19 stack_frame::StackFrame,
20 value::JSValue,
21};
22
23pub const ARG_SCOPE_INDEX: i32 = 1;
27
28pub const ARG_SCOPE_END: i32 = -2;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[repr(u8)]
33pub enum FunctionKind {
34 Normal = 0,
36 Generator = 1,
38 Async = 2,
40 AsyncGenerator = 3,
42}
43
44impl FunctionKind {
45 #[inline(always)]
46 pub const fn is_async(self) -> bool {
47 matches!(self, Self::Async | Self::AsyncGenerator)
48 }
49
50 #[inline(always)]
51 pub const fn is_generator(self) -> bool {
52 matches!(self, Self::Generator | Self::AsyncGenerator)
53 }
54
55 #[inline]
57 pub const fn class_id(&self) -> ClassID {
58 match self {
59 Self::Normal => JS_CLASS_BYTECODE_FUNCTION,
60 Self::Generator => JS_CLASS_GENERATOR_FUNCTION,
61 Self::Async => JS_CLASS_ASYNC_FUNCTION,
62 Self::AsyncGenerator => JS_CLASS_ASYNC_GENERATOR_FUNCTION,
63 }
64 }
65
66 #[inline]
67 pub const fn generator_class_id(&self) -> Option<ClassID> {
68 match self {
69 Self::Generator => Some(JS_CLASS_GENERATOR),
70 Self::AsyncGenerator => Some(JS_CLASS_ASYNC_GENERATOR),
71 Self::Normal | Self::Async => None,
72 }
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77#[repr(u8)]
78pub enum FuncCallType {
79 FUNC_CALL_NORMAL,
80 FUNC_CALL_NEW,
81 FUNC_CALL_SUPER_CTOR,
82 FUNC_CALL_TEMPLATE,
83}
84
85#[derive(Debug)]
91pub struct BCFuncPayload {
92 bytecode: Bytecode,
93
94 pub(crate) var_refs: Vec<JSVarRef>,
96
97 home_object: Option<Object>,
101}
102
103unsafe impl GcTracable for BCFuncPayload {
104 #[inline(always)]
105 fn trace(&self, tracer: &mut gc_lite::GcTracer) {
106 self.bytecode.trace(tracer);
107 self.var_refs.iter().for_each(|x| x.trace(tracer));
108 self.home_object.trace(tracer);
109 }
110}
111
112impl BCFuncPayload {
113 pub fn new(bytecode: Bytecode, var_refs: Vec<JSVarRef>) -> Box<Self> {
114 Box::new(Self {
115 bytecode,
116 var_refs,
117 home_object: None,
118 })
119 }
120
121 #[inline(always)]
123 pub const fn bytecode(&self) -> Bytecode {
124 self.bytecode
125 }
126
127 #[inline(always)]
128 pub const fn home_object(&self) -> Option<Object> {
129 self.home_object
130 }
131
132 pub(crate) const fn set_home_object(&mut self, obj: Option<Object>) {
133 self.home_object = obj;
134 }
135}
136
137#[derive(Debug, Clone, Copy)]
140#[repr(transparent)]
141pub struct BCFunction(pub(super) GcRef<ObjectImpl>);
142
143object_cast!(
144 BCFunction,
145 JS_CLASS_BYTECODE_FUNCTION
146 | JS_CLASS_GENERATOR_FUNCTION
147 | JS_CLASS_ASYNC_FUNCTION
148 | JS_CLASS_ASYNC_GENERATOR_FUNCTION
149);
150
151impl BCFunction {
152 #[inline(always)]
153 pub fn is_async(&self) -> bool {
154 matches!(
155 self.class_id(),
156 JS_CLASS_ASYNC_FUNCTION | JS_CLASS_ASYNC_GENERATOR_FUNCTION
157 )
158 }
159
160 #[inline(always)]
161 pub fn is_generator(&self) -> bool {
162 matches!(
163 self.class_id(),
164 JS_CLASS_GENERATOR_FUNCTION | JS_CLASS_ASYNC_GENERATOR_FUNCTION
165 )
166 }
167
168 #[inline]
170 pub fn payload(&self) -> &BCFuncPayload {
171 unsafe { self.0.as_ref().payload().bc_function().unwrap() }
172 }
173
174 #[inline]
176 pub fn payload_mut(&mut self) -> &mut BCFuncPayload {
177 unsafe { self.0.as_mut().payload_mut().bc_function_mut().unwrap() }
178 }
179
180 pub(crate) fn method_set_home_object(&mut self, home_obj: Option<Object>) {
181 let fo = self.payload_mut();
182 if fo.bytecode().need_home_object() {
183 fo.set_home_object(home_obj);
184 }
185 }
186}
187
188impl ObjectImpl {
189 pub fn ensure_function(&self, ctx: &ContextImpl) -> Result<&Self, i32> {
192 if self.is_function() {
193 Ok(self)
194 } else {
195 ctx.throw_type_error("not a function");
196 Err(-1)
197 }
198 }
199
200 pub(crate) fn realm(&self, default: ContextPtr) -> Result<ContextPtr, i32> {
202 match self.payload() {
203 ObjectPayload::C_FUNCTION(cfunc) => Ok(cfunc
204 .realm
205 .upgrade(default.rt_ptr().gc_heap())
206 .map_or(default, ContextPtr::from)),
207 ObjectPayload::BYTECODE_FUNCTION(fo)
208 | ObjectPayload::GENERATOR_FUNCTION(fo)
209 | ObjectPayload::ASYNC_FUNCTION(fo)
210 | ObjectPayload::ASYNC_GENERATOR_FUNCTION(fo) => {
211 Ok(fo.bytecode().realm().unwrap_or(default))
212 }
213 ObjectPayload::PROXY(px) => {
214 if px.is_revoked() {
215 default.throw_type_error_revoked_proxy();
216 Err(-1)
217 } else {
218 px.target().realm(default)
219 }
220 }
221 ObjectPayload::BOUND_FUNCTION(bfunc) => {
222 if let Some(o) = bfunc.func_obj.as_object() {
223 o.realm(default)
224 } else {
225 Ok(default)
226 }
227 }
228 _ => Ok(default),
229 }
230 }
231}
232
233impl JSValue {
234 pub fn ensure_function(&self, ctx: &ContextImpl) -> Result<&Self, i32> {
237 if self.is_function() {
238 Ok(self)
239 } else {
240 ctx.throw_type_error("not a function");
241 Err(-1)
242 }
243 }
244}
245
246pub fn get_func_name(func: JSValue) -> Option<String> {
247 if let Some(obj) = func.as_object() {
250 if let Some((idx, prs_flags)) = obj.find_shape_property(Atom::CONST_name)
251 && prs_flags & JS_PROP_TMASK == JS_PROP_NORMAL
252 {
253 if let Some(JSProperty::Normal(JSValue::String(s))) = obj.prop_values.get(idx) {
254 return Some(s.content().encode_utf8(false).into_owned());
255 } else {
256 #[cfg(debug_assertions)]
257 unreachable!();
258 #[cfg(not(debug_assertions))]
259 unsafe {
260 std::hint::unreachable_unchecked();
261 }
262 }
263 }
264
265 None
266 } else {
267 None
268 }
269}
270
271pub fn js_function_set_properties(ctx: &ContextImpl, mut func_obj: Object, name: Atom, len: i32) {
273 let _ = func_obj.define_property_value(
274 ctx,
275 Atom::CONST_length,
276 JSValue::Int(len),
277 JS_PROP_CONFIGURABLE, );
279 let _ = func_obj.define_property_value(
280 ctx,
281 Atom::CONST_name,
282 name.force_string(ctx)
283 .map_or(JSValue::EXCEPTION, JSValue::String),
284 JS_PROP_CONFIGURABLE,
285 );
286}
287
288pub fn js_function_proto(_: CallContext) -> JSResult {
289 Ok(JSValue::UNDEFINED)
290}
291
292#[repr(transparent)]
293#[derive(Debug, Clone, Copy)]
294pub struct BytecodeFunction(GcRef<ObjectImpl>);
295
296object_cast!(BytecodeFunction, JS_CLASS_BYTECODE_FUNCTION);
297
298impl BytecodeFunction {
299 #[inline(always)]
300 pub fn payload(&self) -> &BCFuncPayload {
301 unsafe { self.0.as_ref().payload().bc_function().unwrap() }
302 }
303
304 #[inline(always)]
305 pub fn payload_mut(&mut self) -> &mut BCFuncPayload {
306 unsafe { self.0.as_mut().payload_mut().bc_function_mut().unwrap() }
307 }
308
309 pub fn call(
311 mut self,
312 caller_ctx: &ContextImpl,
313 this_obj: JSValue,
314 new_target: JSValue,
315 args: &[JSValue],
316 flags: i32,
317 ) -> JSValue {
318 let rt = caller_ctx.rt_ptr();
319 let bc = self.payload().bytecode();
320
321 let arg_copy_num =
325 if flags & JS_CALL_FLAG_COPY_ARGV != 0 || args.len() < bc.arg_count as usize {
326 std::cmp::max(args.len(), bc.arg_count as usize)
327 } else {
328 0
329 };
330
331 let (buf_values_count, js_mode) = (
333 arg_copy_num as usize + bc.var_count as usize + bc.stack_size as usize,
334 bc.mode(),
335 );
336
337 let mut this_frame = StackFrame::new(self.into(), js_mode);
338 let sf = &mut this_frame;
339
340 if js_check_stack_overflow(&rt, 32) {
342 caller_ctx.throw_stack_overflow();
343 return JSValue::EXCEPTION;
344 }
345
346 if sf.local_buf.try_reserve_exact(buf_values_count).is_err() {
356 caller_ctx.throw_out_of_memory();
357 return JSValue::EXCEPTION;
358 }
359
360 if arg_copy_num != 0 {
362 debug_assert!(arg_copy_num >= args.len());
363
364 sf.local_buf.extend(args.iter().cloned());
366 sf.local_buf.extend(std::iter::repeat_n(
368 JSValue::UNDEFINED,
369 arg_copy_num - args.len(),
370 ));
371
372 sf.arg_count = arg_copy_num as _;
373 sf.arg_real_count = args.len() as _;
374 sf.arg_base_ptr = sf.local_buf.as_mut_ptr()
375 } else {
376 sf.arg_count = args.len() as _;
377 sf.arg_real_count = args.len() as _;
378 sf.arg_base_ptr = args.as_ptr().cast::<JSValue>() as *mut _;
379 }
380
381 sf.local_buf.extend(std::iter::repeat_n(
383 JSValue::UNDEFINED,
384 (bc.var_count + bc.stack_size) as usize,
385 ));
386 debug_assert_eq!(sf.local_buf.len(), buf_values_count);
387
388 sf.var_base_ptr = unsafe { sf.local_buf.as_mut_ptr().add(arg_copy_num as usize) };
389 sf.var_count = bc.var_count;
390
391 let isa = self.payload_mut();
392 bc.run(
393 caller_ctx,
394 isa.home_object,
395 sf,
396 this_obj,
397 new_target,
398 0,
399 0,
400 &mut isa.var_refs,
401 args,
402 false,
403 )
404 }
405
406 pub fn class_constructor(cc: CallContext) -> JSResult {
412 debug_assert!((0..4).contains(&cc.magic()));
413 let mut ctx = cc.ctx_ptr();
414
415 let new_target = *cc.this();
418 let func_kind = unsafe { std::mem::transmute::<u8, FunctionKind>(cc.magic() as u8) };
419
420 let mut buf = SmallVec::<[u8; 256]>::new();
425 buf.push(b'(');
426
427 if matches!(
428 func_kind,
429 FunctionKind::Async | FunctionKind::AsyncGenerator
430 ) {
431 buf.extend_from_slice(b"async ");
432 }
433
434 buf.extend_from_slice(b"function");
435 if matches!(
436 func_kind,
437 FunctionKind::Generator | FunctionKind::AsyncGenerator
438 ) {
439 buf.push(b'*');
440 }
441
442 buf.extend_from_slice(b" anonymous(");
443
444 if let Some((body, arglist)) = cc.arguments().split_last() {
446 for (i, a) in arglist.iter().enumerate() {
448 if i != 0 {
449 buf.push(b',');
450 }
451 buf.extend_from_slice(a.to_string(&ctx)?.content().encode_utf8(false).as_bytes());
452 }
453 buf.extend_from_slice(b"\n) {\n");
454
455 buf.extend_from_slice(
457 body.to_string(&ctx)?
458 .content()
459 .encode_utf8(false)
460 .as_bytes(),
461 );
462 } else {
463 buf.extend_from_slice(b") {\n");
465 }
466
467 buf.extend_from_slice(b"\n})");
468
469 let mut func_val = {
471 let global = ctx.global_obj();
472 ctx.eval_internal(
473 global.into(),
474 buf.as_slice(),
475 "<input>",
476 EvalType::Indirect,
477 EvalFlags::empty(),
478 -1,
479 )?
480 };
481
482 if !new_target.is_undefined() {
483 let mut proto = new_target.get_property(&ctx, Atom::CONST_prototype)?;
485 if !proto.is_object() {
486 proto = new_target
487 .as_object()
488 .map_or(Ok(ctx), |o| o.realm(ctx))?
489 .class_proto(func_kind.class_id())
490 .map_or(JSValue::NULL, JSValue::Object);
491 }
492
493 func_val.set_prototype_internal(&ctx, proto, true)?;
494 }
495
496 Ok(func_val)
497 }
498}