1use crate::host::HostFunction;
2use crate::object::function::JSFunction;
3use crate::object::object::JSObject;
4use crate::runtime::context::JSContext;
5use crate::util::FxHashMap;
6use crate::value::JSValue;
7
8fn throw_type_error(ctx: &mut JSContext, message: &str) {
9 if let Some(ptr) = ctx.get_register_vm_ptr() {
10 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
11 let mut err = JSObject::new_typed(crate::object::object::ObjectType::Error);
12 err.set(
13 ctx.common_atoms.message,
14 JSValue::new_string(ctx.intern(message)),
15 );
16 err.set(
17 ctx.common_atoms.name,
18 JSValue::new_string(ctx.intern("TypeError")),
19 );
20 if let Some(proto) = ctx.get_type_error_prototype() {
21 err.prototype = Some(proto);
22 }
23 let err_ptr = Box::into_raw(Box::new(err)) as usize;
24 ctx.runtime_mut().gc_heap_mut().track(err_ptr);
25 vm.pending_throw = Some(JSValue::new_object(err_ptr));
26 }
27}
28
29fn create_builtin_method(
30 ctx: &mut JSContext,
31 name: &str,
32 arity: u32,
33 display_name: &str,
34) -> JSValue {
35 let mut func = JSFunction::new_builtin(ctx.intern(name), 1);
36 func.set_builtin_marker(ctx, name);
37 if let Some(fn_proto_ptr) = ctx.get_function_prototype() {
38 func.base.set_prototype_raw(fn_proto_ptr);
39 }
40 {
41 let mut desc =
42 crate::object::object::PropertyDescriptor::new_data(JSValue::new_int(arity as i64));
43 desc.writable = false;
44 desc.enumerable = false;
45 desc.configurable = true;
46 func.base
47 .define_property_ext(ctx.common_atoms.length, desc, true, true, true);
48 }
49 {
50 let mut desc = crate::object::object::PropertyDescriptor::new_data(JSValue::new_string(
51 ctx.intern(display_name),
52 ));
53 desc.writable = false;
54 desc.enumerable = false;
55 desc.configurable = true;
56 func.base
57 .define_property_ext(ctx.common_atoms.name, desc, true, true, true);
58 }
59 let ptr = Box::into_raw(Box::new(func)) as usize;
60 ctx.runtime_mut().gc_heap_mut().track_function(ptr);
61 JSValue::new_function(ptr)
62}
63
64fn patch_function_values_with_function_prototype(obj: &mut JSObject, fn_proto_ptr: *mut JSObject) {
65 patch_function_values_recursive(obj, fn_proto_ptr, &mut Vec::new());
66}
67
68fn patch_function_values_recursive(
69 obj: &mut JSObject,
70 fn_proto_ptr: *mut JSObject,
71 visited: &mut Vec<usize>,
72) {
73 let obj_addr = obj as *mut JSObject as usize;
74 if visited.contains(&obj_addr) {
75 return;
76 }
77 visited.push(obj_addr);
78
79 let mut values = Vec::new();
80 obj.for_each_property(|_atom, value, _attrs| {
81 values.push(value);
82 });
83
84 for value in values {
85 if value.is_function() {
86 let func = unsafe { JSValue::function_from_ptr_mut(value.get_ptr()) };
87 if func.base.prototype.is_none() {
88 func.base.set_prototype_raw(fn_proto_ptr);
89 }
90 patch_function_values_recursive(&mut func.base, fn_proto_ptr, visited);
91 } else if value.is_object() {
92 let nested = value.as_object_mut();
93 patch_function_values_recursive(nested, fn_proto_ptr, visited);
94 }
95 }
96}
97
98pub fn init_function(ctx: &mut JSContext) {
99 let mut proto_obj = JSObject::new_function();
100 if let Some(object_proto_ptr) = ctx.get_object_prototype() {
101 proto_obj.prototype = Some(object_proto_ptr);
102 }
103 proto_obj.set(
104 ctx.common_atoms.bind,
105 create_builtin_method(ctx, "function_bind", 1, "bind"),
106 );
107 proto_obj.set(
108 ctx.common_atoms.call,
109 create_builtin_method(ctx, "function_call", 1, "call"),
110 );
111 proto_obj.set(
112 ctx.common_atoms.apply,
113 create_builtin_method(ctx, "function_apply", 2, "apply"),
114 );
115 proto_obj.set(
116 ctx.common_atoms.to_string,
117 create_builtin_method(ctx, "function_toString", 0, "toString"),
118 );
119 {
120 let mut desc = crate::object::object::PropertyDescriptor::new_data(JSValue::new_int(0));
121 desc.writable = false;
122 desc.enumerable = false;
123 desc.configurable = true;
124 proto_obj.define_property_ext(ctx.common_atoms.length, desc, true, true, true);
125 }
126 {
127 let mut desc = crate::object::object::PropertyDescriptor::new_data(JSValue::new_string(
128 ctx.intern(""),
129 ));
130 desc.writable = false;
131 desc.enumerable = false;
132 desc.configurable = true;
133 proto_obj.define_property_ext(ctx.common_atoms.name, desc, true, true, true);
134 }
135 let throw_type_error_fn = {
138 let mut f = JSFunction::new_builtin(ctx.intern("ThrowTypeError"), 0);
139 f.set_builtin_marker(ctx, "throw_type_error_caller");
140 let ptr = Box::into_raw(Box::new(f)) as usize;
141 ctx.runtime_mut().gc_heap_mut().track_function(ptr);
142 JSValue::new_function(ptr)
143 };
144 {
145 let entry = crate::object::object::AccessorEntry {
146 get: Some(throw_type_error_fn.clone()),
147 set: Some(throw_type_error_fn.clone()),
148 enumerable: false,
149 configurable: true,
150 };
151 proto_obj
152 .ensure_extra()
153 .accessors
154 .get_or_insert_with(|| Box::new(FxHashMap::default()))
155 .insert(ctx.intern("caller"), entry);
156 }
157 {
158 let entry = crate::object::object::AccessorEntry {
159 get: Some(throw_type_error_fn.clone()),
160 set: Some(throw_type_error_fn.clone()),
161 enumerable: false,
162 configurable: true,
163 };
164 proto_obj
165 .ensure_extra()
166 .accessors
167 .get_or_insert_with(|| Box::new(FxHashMap::default()))
168 .insert(ctx.intern("arguments"), entry);
169 }
170
171 if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
172 proto_obj.prototype = Some(obj_proto_ptr);
173 }
174
175 let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
176 ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
177 let proto_value = JSValue::new_object(proto_ptr);
178
179 ctx.set_function_prototype(proto_ptr);
180
181 let proto_obj_mut = unsafe { &mut *(proto_ptr as *mut JSObject) };
182 for atom in [
183 ctx.common_atoms.bind,
184 ctx.common_atoms.call,
185 ctx.common_atoms.apply,
186 ctx.common_atoms.to_string,
187 ] {
188 if let Some(v) = proto_obj_mut.get(atom) {
189 if v.is_function() {
190 let f = unsafe { JSValue::function_from_ptr_mut(v.get_ptr()) };
191 f.base.set_prototype_raw(proto_ptr as *mut JSObject);
192 }
193 }
194 }
195
196 let global = ctx.global();
197 if global.is_object() {
198 let global_obj = global.as_object_mut();
199 patch_function_values_with_function_prototype(global_obj, proto_ptr as *mut JSObject);
200 }
201
202 let mut function_ctor = JSFunction::new_builtin(ctx.common_atoms.function, 1);
203 function_ctor.set_builtin_marker(ctx, "function_constructor");
204 function_ctor.base.prototype = Some(proto_ptr as *mut JSObject);
205 function_ctor
206 .base
207 .set(ctx.common_atoms.prototype, proto_value.clone());
208
209 let function_ptr = Box::into_raw(Box::new(function_ctor)) as usize;
210 ctx.runtime_mut().gc_heap_mut().track_function(function_ptr);
211 let function_value = JSValue::new_function(function_ptr);
212
213 unsafe {
214 let proto_obj_ptr = proto_ptr as *mut crate::object::object::JSObject;
215 (*proto_obj_ptr).set(ctx.common_atoms.constructor, function_value);
216 }
217
218 let global = ctx.global();
219 if global.is_object() {
220 let global_obj = global.as_object_mut();
221 crate::builtins::global::set_non_enumerable(
222 global_obj,
223 ctx.common_atoms.function,
224 function_value,
225 );
226 crate::builtins::global::set_non_enumerable(
227 global_obj,
228 ctx.intern("FunctionPrototype"),
229 proto_value,
230 );
231 }
232}
233
234pub fn register_builtins(ctx: &mut JSContext) {
235 ctx.register_builtin(
236 "function_bind",
237 HostFunction::method("bind", 1, function_bind),
238 );
239 ctx.register_builtin(
240 "function_call",
241 HostFunction::method("call", 1, function_call),
242 );
243 ctx.register_builtin(
244 "function_apply",
245 HostFunction::method("apply", 2, function_apply),
246 );
247 ctx.register_builtin(
248 "function_toString",
249 HostFunction::method("toString", 0, function_to_string),
250 );
251 ctx.register_builtin(
252 "function_length",
253 HostFunction::method("length", 0, function_length),
254 );
255 ctx.register_builtin(
256 "function_name",
257 HostFunction::method("name", 0, function_name),
258 );
259 ctx.register_builtin(
260 "function_constructor",
261 HostFunction::ctor("Function", 1, function_constructor),
262 );
263 ctx.register_builtin(
264 "function_has_instance",
265 HostFunction::method(SYMBOL_HAS_INSTANCE_DISPLAY, 1, function_has_instance),
266 );
267 ctx.register_builtin(
268 "throw_type_error_callee",
269 HostFunction::new("callee", 0, throw_type_error_callee),
270 );
271 ctx.register_builtin(
272 "throw_type_error_caller",
273 HostFunction::new("caller", 0, throw_type_error_caller_args),
274 );
275}
276
277fn throw_type_error_callee(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
278 if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
279 let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
280 vm.set_pending_type_error(ctx, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
281 }
282 JSValue::undefined()
283}
284
285fn throw_type_error_caller_args(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
286 if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
287 let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
288 vm.set_pending_type_error(ctx, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
289 }
290 JSValue::undefined()
291}
292
293const SYMBOL_HAS_INSTANCE_DISPLAY: &str = "[Symbol.hasInstance]";
294
295fn function_has_instance(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
296 if args.len() < 2 {
297 return JSValue::bool(false);
298 }
299 let f = &args[0];
300 let v = &args[1];
301 if !f.is_function() && !f.is_object() {
302 return JSValue::bool(false);
303 }
304 if !v.is_object() && !v.is_function() {
305 return JSValue::bool(false);
306 }
307 let proto_atom = ctx.common_atoms.prototype;
308 let proto_ptr = if f.is_function() {
309 let js_func = f.as_function();
310 if !js_func.cached_prototype_ptr.is_null() {
311 js_func.cached_prototype_ptr as *const crate::object::object::JSObject
312 } else {
313 let pv = js_func.base.get(proto_atom).unwrap_or(JSValue::undefined());
314 if pv.is_object() {
315 pv.get_ptr() as *const crate::object::object::JSObject
316 } else {
317 return JSValue::bool(false);
318 }
319 }
320 } else {
321 let pv = f
322 .as_object()
323 .get(proto_atom)
324 .unwrap_or(JSValue::undefined());
325 if pv.is_object() {
326 pv.get_ptr() as *const crate::object::object::JSObject
327 } else {
328 return JSValue::bool(false);
329 }
330 };
331 let obj_ptr = v.get_ptr() as *const crate::object::object::JSObject;
332 let mut current = unsafe { (*obj_ptr).prototype };
333 while let Some(p) = current {
334 if std::ptr::eq(p, proto_ptr) {
335 return JSValue::bool(true);
336 }
337 current = unsafe { (*p).prototype };
338 }
339 JSValue::bool(false)
340}
341
342fn function_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
343 if args.is_empty() {
344 return match crate::eval(ctx, "(function anonymous() {})") {
345 Ok(val) => {
346 if val.is_function() {
347 val.as_function_mut().name = ctx.intern("anonymous");
348 let name_atom = ctx.intern("name");
349 let obj = val.as_object_mut();
350 let mut desc = crate::object::object::PropertyDescriptor::new_data(
351 JSValue::new_string(ctx.intern("anonymous")),
352 );
353 desc.writable = false;
354 desc.enumerable = false;
355 desc.configurable = true;
356 obj.define_property_ext(name_atom, desc, true, true, true);
357 }
358 val
359 }
360 Err(_) => JSValue::undefined(),
361 };
362 }
363 let body_idx = args.len() - 1;
364 let body_str = if args[body_idx].is_string() {
365 ctx.get_atom_str(args[body_idx].get_atom()).to_string()
366 } else {
367 String::new()
368 };
369 let mut params = Vec::new();
370 for i in 0..body_idx {
371 let p = if args[i].is_string() {
372 ctx.get_atom_str(args[i].get_atom()).to_string()
373 } else {
374 String::new()
375 };
376 for part in p.trim().split(',') {
377 let part = part.trim();
378 if !part.is_empty() {
379 params.push(part.to_string());
380 }
381 }
382 }
383 let params_str = params.join(",");
384 let source = format!("(function anonymous({}){{{}}})", params_str, body_str);
385 match crate::eval(ctx, &source) {
386 Ok(val) => {
387 if val.is_function() {
388 val.as_function_mut().name = ctx.intern("anonymous");
389 let name_atom = ctx.intern("name");
390 let obj = val.as_object_mut();
391 let mut desc = crate::object::object::PropertyDescriptor::new_data(
392 JSValue::new_string(ctx.intern("anonymous")),
393 );
394 desc.writable = false;
395 desc.enumerable = false;
396 desc.configurable = true;
397 obj.define_property_ext(name_atom, desc, true, true, true);
398 }
399 val
400 }
401 Err(e) => {
402 let mut err = crate::object::object::JSObject::new();
403 err.set(
404 ctx.intern("name"),
405 JSValue::new_string(ctx.intern("SyntaxError")),
406 );
407 err.set(ctx.intern("message"), JSValue::new_string(ctx.intern(&e)));
408 if let Some(proto) = ctx.get_syntax_error_prototype() {
409 err.prototype = Some(proto);
410 }
411 let ptr = Box::into_raw(Box::new(err)) as usize;
412 ctx.runtime_mut().gc_heap_mut().track(ptr);
413 ctx.pending_exception = Some(JSValue::new_object(ptr));
414 JSValue::undefined()
415 }
416 }
417}
418
419fn function_bind(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
420 if args.is_empty() {
421 return JSValue::undefined();
422 }
423
424 let this_val = &args[0];
425 if !this_val.is_function() && !this_val.is_object() {
426 let mut err =
427 crate::object::object::JSObject::new_typed(crate::object::object::ObjectType::Error);
428 if let Some(proto) = ctx.get_type_error_prototype() {
429 err.prototype = Some(proto);
430 }
431 err.set(
432 ctx.common_atoms.message,
433 JSValue::new_string(ctx.intern("this is not a function")),
434 );
435 let ptr = Box::into_raw(Box::new(err)) as usize;
436 ctx.runtime_mut().gc_heap_mut().track(ptr);
437 ctx.pending_exception = Some(JSValue::new_object(ptr));
438 return JSValue::undefined();
439 }
440
441 let is_bound = this_val.is_object() && !this_val.is_function();
442 if is_bound {
443 let obj = this_val.as_object();
444 if obj.get(ctx.common_atoms.__boundFn).is_none() {
445 let mut err = crate::object::object::JSObject::new_typed(
446 crate::object::object::ObjectType::Error,
447 );
448 if let Some(proto) = ctx.get_type_error_prototype() {
449 err.prototype = Some(proto);
450 }
451 err.set(
452 ctx.common_atoms.message,
453 JSValue::new_string(ctx.intern("this is not a function")),
454 );
455 let eptr = Box::into_raw(Box::new(err)) as usize;
456 ctx.runtime_mut().gc_heap_mut().track(eptr);
457 ctx.pending_exception = Some(JSValue::new_object(eptr));
458 return JSValue::undefined();
459 }
460 }
461
462 let this_arg = if args.len() > 1 {
463 args[1].clone()
464 } else {
465 JSValue::undefined()
466 };
467
468 let mut wrapper = JSObject::new();
469 if let Some(proto_ptr) = ctx.get_function_prototype() {
470 wrapper.prototype = Some(proto_ptr);
471 }
472 wrapper.set(ctx.common_atoms.__boundFn, *this_val);
473 wrapper.set(ctx.common_atoms.__boundThis, this_arg);
474
475 let mut args_arr = JSObject::new_array();
476 let length_key = ctx.common_atoms.length;
477 let bound_count = if args.len() > 2 {
478 (args.len() - 2) as i64
479 } else {
480 0
481 };
482 args_arr.set(length_key, JSValue::new_int(bound_count));
483 let mut idx = 0;
484 for arg in args.iter().skip(2) {
485 let key = ctx.intern(&idx.to_string());
486 args_arr.set(key, arg.clone());
487 idx += 1;
488 }
489 let boxed_args_arr = Box::new(args_arr);
490 let args_ptr = Box::into_raw(boxed_args_arr) as usize;
491 ctx.runtime_mut().gc_heap_mut().track(args_ptr);
492 wrapper.set(ctx.common_atoms.__boundArgs, JSValue::new_object(args_ptr));
493
494 let target_length = if this_val.is_function() {
495 this_val.as_function().arity as i64
496 } else {
497 let obj = this_val.as_object();
498 if let Some(len) = obj.get(ctx.common_atoms.length) {
499 if len.is_int() { len.get_int() } else { 0 }
500 } else {
501 0
502 }
503 };
504 let bound_length = 0i64.max(target_length - bound_count);
505 wrapper.define_property(
506 ctx.common_atoms.length,
507 crate::object::object::PropertyDescriptor {
508 value: Some(JSValue::new_int(bound_length)),
509 writable: false,
510 enumerable: false,
511 configurable: true,
512 get: None,
513 set: None,
514 },
515 );
516
517 let target_name = if this_val.is_function() {
518 ctx.get_atom_str(this_val.as_function().name).to_string()
519 } else {
520 let obj = this_val.as_object();
521 if let Some(n) = obj.get(ctx.common_atoms.name) {
522 if n.is_string() {
523 ctx.get_atom_str(n.get_atom()).to_string()
524 } else {
525 String::new()
526 }
527 } else {
528 String::new()
529 }
530 };
531 let bound_name = format!("bound {}", target_name);
532 wrapper.define_property(
533 ctx.common_atoms.name,
534 crate::object::object::PropertyDescriptor {
535 value: Some(JSValue::new_string(ctx.intern(&bound_name))),
536 writable: false,
537 enumerable: false,
538 configurable: true,
539 get: None,
540 set: None,
541 },
542 );
543
544 let boxed_wrapper = Box::new(wrapper);
545 let ptr = Box::into_raw(boxed_wrapper) as usize;
546 ctx.runtime_mut().gc_heap_mut().track(ptr);
547 JSValue::new_object(ptr)
548}
549
550fn function_call(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
551 if args.is_empty() {
552 return JSValue::undefined();
553 }
554 let this_val = &args[0];
555 if !this_val.is_function() {
556 throw_type_error(ctx, "this is not a function");
557 return JSValue::undefined();
558 }
559 let this_arg = if args.len() > 1 {
560 args[1].clone()
561 } else {
562 JSValue::undefined()
563 };
564 let mut call_args = Vec::new();
565 for arg in args.iter().skip(2) {
566 call_args.push(arg.clone());
567 }
568
569 if let Some(ptr) = ctx.get_register_vm_ptr() {
570 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
571 let result = vm.call_function_with_this(ctx, *this_val, this_arg, &call_args);
572 match result {
573 Ok(val) => val,
574 Err(msg) => {
575 let exc = vm.last_caught_exception.take()
576 .or_else(|| ctx.pending_exception.take())
577 .unwrap_or_else(|| {
578 let mut err = crate::object::object::JSObject::new_typed(
579 crate::object::object::ObjectType::Error,
580 );
581 let msg_only = if let Some(idx) = msg.find(": ") {
582 msg[idx + 2..].to_string()
583 } else {
584 msg.clone()
585 };
586 err.set(
587 ctx.common_atoms.message,
588 JSValue::new_string(ctx.intern(&msg_only)),
589 );
590 if msg.contains("TypeError") {
591 err.set(
592 ctx.common_atoms.name,
593 JSValue::new_string(ctx.intern("TypeError")),
594 );
595 if let Some(proto) = ctx.get_type_error_prototype() {
596 err.prototype = Some(proto);
597 }
598 } else if msg.contains("RangeError") {
599 err.set(
600 ctx.common_atoms.name,
601 JSValue::new_string(ctx.intern("RangeError")),
602 );
603 if let Some(proto) = ctx.get_range_error_prototype() {
604 err.prototype = Some(proto);
605 }
606 } else {
607 err.set(
608 ctx.common_atoms.name,
609 JSValue::new_string(ctx.intern("Error")),
610 );
611 if let Some(proto) = ctx.get_error_prototype() {
612 err.prototype = Some(proto);
613 }
614 }
615 let err_ptr = Box::into_raw(Box::new(err)) as usize;
616 ctx.runtime_mut().gc_heap_mut().track(err_ptr);
617 JSValue::new_object(err_ptr)
618 });
619 vm.pending_throw = Some(exc);
620 JSValue::undefined()
621 }
622 }
623 } else {
624 JSValue::undefined()
625 }
626}
627
628fn function_apply(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
629 if args.is_empty() {
630 return JSValue::undefined();
631 }
632 let this_val = &args[0];
633 if !this_val.is_function() {
634 throw_type_error(ctx, "this is not a function");
635 return JSValue::undefined();
636 }
637
638 let this_arg = if args.len() > 1 {
639 args[1].clone()
640 } else {
641 JSValue::undefined()
642 };
643
644 let mut call_args_buf = [JSValue::undefined(); 16];
645 let mut call_args_vec = Vec::new();
646 let call_args: &[JSValue];
647
648 if args.len() > 2 && args[2].is_object_like() {
649 let arr_obj = args[2].as_object();
650 let length_atom = ctx.common_atoms.length;
651
652 let len_val = arr_obj.get(length_atom);
653 if let Some(lv) = len_val {
654 if !lv.is_int() {
655 call_args = &[];
656 if let Some(ptr) = ctx.get_register_vm_ptr() {
657 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
658 let result = vm.call_function_with_this(ctx, *this_val, this_arg, call_args);
659 return match result {
660 Ok(val) => val,
661 Err(_) => {
662 if let Some(exc) = vm.last_caught_exception.take() {
663 vm.pending_throw = Some(exc);
664 }
665 JSValue::undefined()
666 }
667 };
668 }
669 return JSValue::undefined();
670 }
671 let len = lv.get_int() as usize;
672
673 if arr_obj.is_mapped_arguments() {
674 let fi = arr_obj.mapped_args_frame_index();
675 let param_count = arr_obj.mapped_args_param_count() as usize;
676 let vm_ptr = ctx.get_register_vm_ptr();
677 if let Some(ptr) = vm_ptr {
678 let vm = unsafe { &*(ptr as *const crate::runtime::vm::VM) };
679 if fi < vm.frames.len() {
680 let frame = &vm.frames[fi];
681 let base = frame.registers_base;
682 let target = if len <= 16 {
683 &mut call_args_buf[..len]
684 } else {
685 call_args_vec.resize(len, JSValue::undefined());
686 &mut call_args_vec[..len]
687 };
688 let saved = &frame.saved_args;
689 for i in 0..len {
690 target[i] = if i < param_count {
691 let reg_idx = base + 1 + i;
692 if reg_idx < vm.registers.len() {
693 vm.registers[reg_idx]
694 } else {
695 JSValue::undefined()
696 }
697 } else if i < saved.len() {
698 saved[i]
699 } else {
700 arr_obj.get_indexed(i).unwrap_or(JSValue::undefined())
701 };
702 }
703 call_args = if len <= 16 {
704 &call_args_buf[..len]
705 } else {
706 &call_args_vec[..len]
707 };
708 } else {
709 call_args = &[];
710 }
711 } else {
712 call_args = &[];
713 }
714 } else if len <= 16 {
715 if arr_obj.is_array() {
716 let ptr = arr_obj as *const _ as usize;
717 if arr_obj.is_dense_array() {
718 let arr_ptr =
719 unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
720 for i in 0..len {
721 call_args_buf[i] = arr_ptr.get(i).unwrap_or(JSValue::undefined());
722 }
723 } else {
724 for i in 0..len {
725 let key = ctx.int_atom_mut(i);
726 call_args_buf[i] = arr_obj.get(key).unwrap_or(JSValue::undefined());
727 }
728 }
729 } else if let Some(slice) = arr_obj.get_dense_slice(len) {
730 call_args_buf[..len].copy_from_slice(slice);
731 } else {
732 for i in 0..len {
733 if let Some(val) = arr_obj.get_indexed(i) {
734 call_args_buf[i] = val;
735 } else {
736 let key = ctx.int_atom_mut(i);
737 call_args_buf[i] = arr_obj.get(key).unwrap_or(JSValue::undefined());
738 }
739 }
740 }
741 call_args = &call_args_buf[..len];
742 } else {
743 if arr_obj.is_array() {
744 let ptr = arr_obj as *const _ as usize;
745 if arr_obj.is_dense_array() {
746 let arr_ptr =
747 unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
748 for i in 0..len {
749 call_args_vec.push(arr_ptr.get(i).unwrap_or(JSValue::undefined()));
750 }
751 } else {
752 for i in 0..len {
753 let key = ctx.int_atom_mut(i);
754 call_args_vec.push(arr_obj.get(key).unwrap_or(JSValue::undefined()));
755 }
756 }
757 } else if let Some(slice) = arr_obj.get_dense_slice(len) {
758 call_args_vec.extend_from_slice(slice);
759 } else {
760 for i in 0..len {
761 if let Some(val) = arr_obj.get_indexed(i) {
762 call_args_vec.push(val);
763 } else {
764 let key = ctx.int_atom_mut(i);
765 if let Some(val) = arr_obj.get(key) {
766 call_args_vec.push(val);
767 } else {
768 call_args_vec.push(JSValue::undefined());
769 }
770 }
771 }
772 }
773 call_args = &call_args_vec;
774 }
775 } else {
776 call_args = &[];
777 }
778 } else if args.len() > 2
779 && !args[2].is_null()
780 && !args[2].is_undefined()
781 && !args[2].is_object_like()
782 {
783 throw_type_error(ctx, "CreateListFromArrayLike called on non-object");
784 return JSValue::undefined();
785 } else {
786 call_args = &[];
787 }
788
789 if let Some(ptr) = ctx.get_register_vm_ptr() {
790 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
791 let result = vm.call_function_with_this(ctx, *this_val, this_arg, call_args);
792 match result {
793 Ok(val) => val,
794 Err(_) => {
795 if let Some(exc) = vm.last_caught_exception.take() {
796 vm.pending_throw = Some(exc);
797 }
798 JSValue::undefined()
799 }
800 }
801 } else {
802 JSValue::undefined()
803 }
804}
805
806fn function_to_string(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
807 if args.is_empty() {
808 return JSValue::new_string(ctx.intern("function () { [native code] }"));
809 }
810 let this_val = &args[0];
811 if this_val.is_function() {
812 let js_func = this_val.as_function();
813
814 let func_name = ctx.get_atom_str(js_func.name).to_string();
815 let arity = js_func.arity as usize;
816
817 let params: Vec<String> = (0..arity).map(|i| format!("a{}", i)).collect();
818 let _param_str = params.join(", ");
819
820 if js_func.is_builtin() {
821 if func_name.is_empty() {
822 JSValue::new_string(ctx.intern(&format!("function() {{ [native code] }}")))
823 } else {
824 JSValue::new_string(
825 ctx.intern(&format!("function {}() {{ [native code] }}", func_name)),
826 )
827 }
828 } else {
829 let prefix = if js_func.is_async() { "async " } else { "" };
830 let suffix = if js_func.is_generator() { "*" } else { "" };
831 if func_name.is_empty() {
832 JSValue::new_string(ctx.intern(&format!(
833 "{}function{}() {{ [native code] }}",
834 prefix, suffix
835 )))
836 } else {
837 JSValue::new_string(ctx.intern(&format!(
838 "{}function{} {}() {{ [native code] }}",
839 prefix, suffix, func_name
840 )))
841 }
842 }
843 } else {
844 JSValue::new_string(ctx.intern("[object Function]"))
845 }
846}
847
848fn function_length(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
849 if args.is_empty() {
850 return JSValue::undefined();
851 }
852 let this_val = &args[0];
853 if !this_val.is_function() {
854 return JSValue::undefined();
855 }
856 let js_func = this_val.as_function();
857 JSValue::new_int(js_func.arity as i64)
858}
859
860fn function_name(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
861 if args.is_empty() {
862 return JSValue::undefined();
863 }
864 let this_val = &args[0];
865 if !this_val.is_function() {
866 return JSValue::undefined();
867 }
868 let js_func = this_val.as_function();
869 let name_str = ctx.get_atom_str(js_func.name).to_string();
870 JSValue::new_string(ctx.intern(&name_str))
871}