Skip to main content

pipa/builtins/
error.rs

1use crate::host::HostFunction;
2use crate::object::object::JSObject;
3use crate::runtime::context::JSContext;
4use crate::value::JSValue;
5
6fn set_ctor_prototype(obj: &mut JSObject, key: crate::runtime::atom::Atom, val: JSValue) {
7    obj.define_property(
8        key,
9        crate::object::object::PropertyDescriptor {
10            value: Some(val),
11            writable: false,
12            enumerable: false,
13            configurable: false,
14            get: None,
15            set: None,
16        },
17    );
18}
19
20fn set_own_ne(obj: &mut JSObject, key: crate::runtime::atom::Atom, val: JSValue) {
21    obj.define_property(
22        key,
23        crate::object::object::PropertyDescriptor {
24            value: Some(val),
25            writable: true,
26            enumerable: false,
27            configurable: true,
28            get: None,
29            set: None,
30        },
31    );
32}
33
34fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
35    let arity = ctx.get_builtin_arity(name).unwrap_or(1);
36    let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern(name), arity);
37    func.set_builtin_marker(ctx, name);
38    let ptr = Box::into_raw(Box::new(func)) as usize;
39    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
40    JSValue::new_function(ptr)
41}
42
43pub fn init_error(ctx: &mut JSContext) {
44    fn set_ne(
45        obj: &mut crate::object::object::JSObject,
46        key: crate::runtime::atom::Atom,
47        val: crate::value::JSValue,
48    ) {
49        obj.define_property(
50            key,
51            crate::object::object::PropertyDescriptor {
52                value: Some(val),
53                writable: true,
54                enumerable: false,
55                configurable: true,
56                get: None,
57                set: None,
58            },
59        );
60    }
61
62    let error_atom = ctx.intern("Error");
63
64    let mut error_func = crate::object::function::JSFunction::new_builtin(error_atom, 1);
65    error_func.set_builtin_marker(ctx, "error_constructor");
66    if let Some(func_proto) = ctx.get_function_prototype() {
67        error_func.base.prototype = Some(func_proto);
68    }
69    let error_ptr = Box::into_raw(Box::new(error_func)) as usize;
70    ctx.runtime_mut().gc_heap_mut().track_function(error_ptr);
71    let error_value = JSValue::new_function(error_ptr);
72
73    let global = ctx.global();
74    if global.is_object() {
75        let global_obj = global.as_object_mut();
76        crate::builtins::global::set_non_enumerable(global_obj, error_atom, error_value);
77    }
78
79    let mut proto_obj = JSObject::new();
80    set_ne(
81        &mut proto_obj,
82        ctx.intern("name"),
83        JSValue::new_string(ctx.intern("Error")),
84    );
85    set_ne(
86        &mut proto_obj,
87        ctx.intern("message"),
88        JSValue::new_string(ctx.intern("")),
89    );
90    set_ne(
91        &mut proto_obj,
92        ctx.intern("toString"),
93        create_builtin_function(ctx, "error_toString"),
94    );
95
96    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
97        proto_obj.prototype = Some(obj_proto_ptr);
98    }
99
100    set_ne(&mut proto_obj, ctx.intern("constructor"), error_value);
101
102    let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
103    ctx.set_error_prototype(proto_ptr);
104    let proto_value = JSValue::new_object(proto_ptr);
105    if ctx.get_builtin_func("error_constructor").is_some() {
106        let f = unsafe { JSValue::function_from_ptr_mut(error_ptr) };
107        set_ctor_prototype(&mut f.base, ctx.intern("prototype"), proto_value);
108    }
109
110    let proto_atom = ctx.intern("ErrorPrototype");
111    if global.is_object() {
112        let global_obj = global.as_object_mut();
113        crate::builtins::global::set_non_enumerable(global_obj, proto_atom, proto_value);
114    }
115
116    init_type_error(ctx);
117    init_reference_error(ctx);
118    init_syntax_error(ctx);
119    init_range_error(ctx);
120    init_uri_error(ctx);
121    init_eval_error(ctx);
122
123    let global = ctx.global();
124    if global.is_object() {
125        let error_ctor_val = global.as_object().get(error_atom);
126        if let Some(error_ctor) = error_ctor_val {
127            if error_ctor.is_function() {
128                let error_ctor_ptr = error_ctor.get_ptr() as *mut crate::object::object::JSObject;
129                for name in ["TypeError", "ReferenceError", "SyntaxError", "RangeError", "URIError", "EvalError"] {
130                    let atom = ctx.intern(name);
131                    if let Some(ctor_val) = global.as_object().get(atom) {
132                        if ctor_val.is_function() {
133                            let ctor = unsafe { JSValue::function_from_ptr_mut(ctor_val.get_ptr()) };
134                            ctor.base.set_prototype_raw(error_ctor_ptr);
135                        }
136                    }
137                }
138            }
139        }
140    }
141}
142
143fn init_type_error(ctx: &mut JSContext) {
144    fn set_ne(
145        obj: &mut crate::object::object::JSObject,
146        key: crate::runtime::atom::Atom,
147        val: crate::value::JSValue,
148    ) {
149        obj.define_property(
150            key,
151            crate::object::object::PropertyDescriptor {
152                value: Some(val),
153                writable: true,
154                enumerable: false,
155                configurable: true,
156                get: None,
157                set: None,
158            },
159        );
160    }
161
162    let atom = ctx.intern("TypeError");
163    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
164    ctor.set_builtin_marker(ctx, "type_error_constructor");
165    if let Some(func_proto) = ctx.get_function_prototype() {
166        ctor.base.prototype = Some(func_proto);
167    }
168    let ptr = Box::into_raw(Box::new(ctor)) as usize;
169    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
170    let value = JSValue::new_function(ptr);
171
172    let global = ctx.global();
173    if global.is_object() {
174        let global_obj = global.as_object_mut();
175        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
176    }
177
178    let mut proto = JSObject::new();
179    set_ne(
180        &mut proto,
181        ctx.intern("name"),
182        JSValue::new_string(ctx.intern("TypeError")),
183    );
184    set_ne(
185        &mut proto,
186        ctx.intern("message"),
187        JSValue::new_string(ctx.intern("")),
188    );
189
190    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
191        proto.prototype = Some(error_proto_ptr);
192    }
193    set_ne(&mut proto, ctx.intern("constructor"), value);
194
195    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
196    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
197    let proto_value = JSValue::new_object(proto_ptr);
198    ctx.set_type_error_prototype(proto_ptr);
199    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
200    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
201}
202
203fn init_reference_error(ctx: &mut JSContext) {
204    fn set_ne(
205        obj: &mut crate::object::object::JSObject,
206        key: crate::runtime::atom::Atom,
207        val: crate::value::JSValue,
208    ) {
209        obj.define_property(
210            key,
211            crate::object::object::PropertyDescriptor {
212                value: Some(val),
213                writable: true,
214                enumerable: false,
215                configurable: true,
216                get: None,
217                set: None,
218            },
219        );
220    }
221
222    let atom = ctx.intern("ReferenceError");
223    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
224    ctor.set_builtin_marker(ctx, "reference_error_constructor");
225    if let Some(func_proto) = ctx.get_function_prototype() {
226        ctor.base.prototype = Some(func_proto);
227    }
228    let ptr = Box::into_raw(Box::new(ctor)) as usize;
229    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
230    let value = JSValue::new_function(ptr);
231
232    let mut proto = JSObject::new();
233    set_ne(
234        &mut proto,
235        ctx.intern("name"),
236        JSValue::new_string(ctx.intern("ReferenceError")),
237    );
238    set_ne(
239        &mut proto,
240        ctx.intern("message"),
241        JSValue::new_string(ctx.intern("")),
242    );
243    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
244        proto.prototype = Some(error_proto_ptr);
245    }
246    set_ne(&mut proto, ctx.intern("constructor"), value);
247
248    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
249    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
250    let proto_value = JSValue::new_object(proto_ptr);
251    ctx.set_reference_error_prototype(proto_ptr);
252    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
253    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
254
255    let global = ctx.global();
256    if global.is_object() {
257        let global_obj = global.as_object_mut();
258        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
259    }
260}
261
262fn init_syntax_error(ctx: &mut JSContext) {
263    fn set_ne(
264        obj: &mut crate::object::object::JSObject,
265        key: crate::runtime::atom::Atom,
266        val: crate::value::JSValue,
267    ) {
268        obj.define_property(
269            key,
270            crate::object::object::PropertyDescriptor {
271                value: Some(val),
272                writable: true,
273                enumerable: false,
274                configurable: true,
275                get: None,
276                set: None,
277            },
278        );
279    }
280
281    let atom = ctx.intern("SyntaxError");
282    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
283    ctor.set_builtin_marker(ctx, "syntax_error_constructor");
284    if let Some(func_proto) = ctx.get_function_prototype() {
285        ctor.base.prototype = Some(func_proto);
286    }
287    let ptr = Box::into_raw(Box::new(ctor)) as usize;
288    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
289    let value = JSValue::new_function(ptr);
290
291    let mut proto = JSObject::new();
292    set_ne(
293        &mut proto,
294        ctx.intern("name"),
295        JSValue::new_string(ctx.intern("SyntaxError")),
296    );
297    set_ne(
298        &mut proto,
299        ctx.intern("message"),
300        JSValue::new_string(ctx.intern("")),
301    );
302    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
303        proto.prototype = Some(error_proto_ptr);
304    }
305    set_ne(&mut proto, ctx.intern("constructor"), value);
306
307    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
308    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
309    let proto_value = JSValue::new_object(proto_ptr);
310    ctx.set_syntax_error_prototype(proto_ptr);
311    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
312    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
313
314    let global = ctx.global();
315    if global.is_object() {
316        let global_obj = global.as_object_mut();
317        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
318    }
319}
320
321fn init_range_error(ctx: &mut JSContext) {
322    fn set_ne(
323        obj: &mut crate::object::object::JSObject,
324        key: crate::runtime::atom::Atom,
325        val: crate::value::JSValue,
326    ) {
327        obj.define_property(
328            key,
329            crate::object::object::PropertyDescriptor {
330                value: Some(val),
331                writable: true,
332                enumerable: false,
333                configurable: true,
334                get: None,
335                set: None,
336            },
337        );
338    }
339
340    let atom = ctx.intern("RangeError");
341    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
342    ctor.set_builtin_marker(ctx, "range_error_constructor");
343    if let Some(func_proto) = ctx.get_function_prototype() {
344        ctor.base.prototype = Some(func_proto);
345    }
346    let ptr = Box::into_raw(Box::new(ctor)) as usize;
347    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
348    let value = JSValue::new_function(ptr);
349
350    let mut proto = JSObject::new();
351    set_ne(
352        &mut proto,
353        ctx.intern("name"),
354        JSValue::new_string(ctx.intern("RangeError")),
355    );
356    set_ne(
357        &mut proto,
358        ctx.intern("message"),
359        JSValue::new_string(ctx.intern("")),
360    );
361    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
362        proto.prototype = Some(error_proto_ptr);
363    }
364    set_ne(&mut proto, ctx.intern("constructor"), value);
365
366    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
367    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
368    let proto_value = JSValue::new_object(proto_ptr);
369    ctx.set_range_error_prototype(proto_ptr);
370    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
371    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
372
373    let global = ctx.global();
374    if global.is_object() {
375        let global_obj = global.as_object_mut();
376        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
377    }
378}
379
380fn init_uri_error(ctx: &mut JSContext) {
381    fn set_ne(
382        obj: &mut crate::object::object::JSObject,
383        key: crate::runtime::atom::Atom,
384        val: crate::value::JSValue,
385    ) {
386        obj.define_property(
387            key,
388            crate::object::object::PropertyDescriptor {
389                value: Some(val),
390                writable: true,
391                enumerable: false,
392                configurable: true,
393                get: None,
394                set: None,
395            },
396        );
397    }
398
399    let atom = ctx.intern("URIError");
400    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
401    ctor.set_builtin_marker(ctx, "uri_error_constructor");
402    if let Some(func_proto) = ctx.get_function_prototype() {
403        ctor.base.prototype = Some(func_proto);
404    }
405    let ptr = Box::into_raw(Box::new(ctor)) as usize;
406    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
407    let value = JSValue::new_function(ptr);
408
409    let global = ctx.global();
410    if global.is_object() {
411        let global_obj = global.as_object_mut();
412        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
413    }
414
415    let mut proto = JSObject::new();
416    set_ne(
417        &mut proto,
418        ctx.intern("name"),
419        JSValue::new_string(ctx.intern("URIError")),
420    );
421    set_ne(
422        &mut proto,
423        ctx.intern("message"),
424        JSValue::new_string(ctx.intern("")),
425    );
426
427    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
428        proto.prototype = Some(error_proto_ptr);
429    }
430    set_ne(&mut proto, ctx.intern("constructor"), value);
431
432    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
433    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
434    let proto_value = JSValue::new_object(proto_ptr);
435    ctx.set_uri_error_prototype(proto_ptr);
436    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
437    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
438}
439
440fn init_eval_error(ctx: &mut JSContext) {
441    fn set_ne(
442        obj: &mut crate::object::object::JSObject,
443        key: crate::runtime::atom::Atom,
444        val: crate::value::JSValue,
445    ) {
446        obj.define_property(
447            key,
448            crate::object::object::PropertyDescriptor {
449                value: Some(val),
450                writable: true,
451                enumerable: false,
452                configurable: true,
453                get: None,
454                set: None,
455            },
456        );
457    }
458
459    let atom = ctx.intern("EvalError");
460    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
461    ctor.set_builtin_marker(ctx, "eval_error_constructor");
462    if let Some(func_proto) = ctx.get_function_prototype() {
463        ctor.base.prototype = Some(func_proto);
464    }
465    let ptr = Box::into_raw(Box::new(ctor)) as usize;
466    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
467    let value = JSValue::new_function(ptr);
468
469    let global = ctx.global();
470    if global.is_object() {
471        let global_obj = global.as_object_mut();
472        crate::builtins::global::set_non_enumerable(global_obj, atom, value);
473    }
474
475    let mut proto = JSObject::new();
476    set_ne(
477        &mut proto,
478        ctx.intern("name"),
479        JSValue::new_string(ctx.intern("EvalError")),
480    );
481    set_ne(
482        &mut proto,
483        ctx.intern("message"),
484        JSValue::new_string(ctx.intern("")),
485    );
486
487    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
488        proto.prototype = Some(error_proto_ptr);
489    }
490    set_ne(&mut proto, ctx.intern("constructor"), value);
491
492    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
493    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
494    let proto_value = JSValue::new_object(proto_ptr);
495    ctx.set_eval_error_prototype(proto_ptr);
496    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
497    set_ctor_prototype(&mut ctor.base, ctx.intern("prototype"), proto_value);
498}
499
500fn get_property(
501    ctx: &mut JSContext,
502    obj: &crate::object::object::JSObject,
503    prop: crate::runtime::atom::Atom,
504    this: &JSValue,
505) -> Option<JSValue> {
506    if let Some(val) = obj.get_own_accessor_value(prop) {
507        if val.is_function() {
508            if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
509                let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
510                let saved_exception = ctx.pending_exception.take();
511                let result = vm.call_function_with_this(ctx, val, this.clone(), &[]);
512                if result.is_err() {
513                    let exc = ctx
514                        .pending_exception
515                        .take()
516                        .or_else(|| vm.last_caught_exception.take())
517                        .or(saved_exception);
518                    if let Some(e) = exc {
519                        ctx.pending_exception = Some(e);
520                    }
521                    return None;
522                }
523                ctx.pending_exception = saved_exception;
524                return result.ok();
525            }
526        }
527        return Some(val);
528    }
529    obj.get(prop)
530}
531
532fn js_to_string_value(ctx: &mut JSContext, val: &JSValue) -> Result<String, ()> {
533    if val.is_string() {
534        return Ok(ctx.get_atom_str(val.get_atom()).to_string());
535    }
536    if val.is_undefined() {
537        return Ok("undefined".to_string());
538    }
539    if val.is_null() {
540        return Ok("null".to_string());
541    }
542    if val.is_bool() {
543        if val.is_truthy() {
544            return Ok("true".to_string());
545        }
546        return Ok("false".to_string());
547    }
548    if val.is_int() {
549        let n = val.get_int();
550        return Ok(format!("{}", n));
551    }
552    if val.is_float() {
553        let n = val.to_number();
554        if n == n.floor() && n.is_finite() && n.abs() < 1e15 {
555            return Ok(format!("{}", n as i64));
556        }
557        return Ok(format!("{}", n));
558    }
559    if val.is_symbol() {
560        let mut err = JSObject::new_typed(crate::object::object::ObjectType::Error);
561        if let Some(proto) = ctx.get_type_error_prototype() {
562            err.prototype = Some(proto);
563        }
564        err.set(
565            ctx.common_atoms.message,
566            JSValue::new_string(ctx.intern("Cannot convert a Symbol value to a string")),
567        );
568        let ptr = Box::into_raw(Box::new(err)) as usize;
569        ctx.runtime_mut().gc_heap_mut().track(ptr);
570        ctx.pending_exception = Some(JSValue::new_object(ptr));
571        return Err(());
572    }
573    if val.is_object() {
574        let obj = val.as_object();
575        if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
576            let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
577            let to_string_atom = ctx.intern("toString");
578            let value_of_atom = ctx.intern("valueOf");
579            let hint_string = ctx.intern("string");
580            if let Some(to_prim) = obj.get(ctx.intern("__toPrimitive__hook__")) {
581                let _ = obj;
582                if to_prim.is_function() {
583                    let result = vm.call_function_with_this(
584                        ctx,
585                        to_prim,
586                        val.clone(),
587                        &[JSValue::new_string(hint_string)],
588                    );
589                    if ctx.pending_exception.is_some() {
590                        return Err(());
591                    }
592                    if let Ok(prim) = result {
593                        if !prim.is_object() {
594                            return js_to_string_value(ctx, &prim);
595                        }
596                    }
597                }
598            }
599            let to_string_fn = obj.get(to_string_atom);
600            let _ = obj;
601            if let Some(fn_val) = to_string_fn {
602                if fn_val.is_function() {
603                    let result = vm.call_function_with_this(ctx, fn_val, val.clone(), &[]);
604                    if ctx.pending_exception.is_some() {
605                        return Err(());
606                    }
607                    if let Ok(r) = result {
608                        if !r.is_object() {
609                            return js_to_string_value(ctx, &r);
610                        }
611                    }
612                }
613            }
614            if ctx.pending_exception.is_some() {
615                return Err(());
616            }
617            let obj2 = val.as_object();
618            let value_of_fn = obj2.get(value_of_atom);
619            let _ = obj2;
620            if let Some(fn_val) = value_of_fn {
621                if fn_val.is_function() {
622                    let result = vm.call_function_with_this(ctx, fn_val, val.clone(), &[]);
623                    if ctx.pending_exception.is_some() {
624                        return Err(());
625                    }
626                    if let Ok(r) = result {
627                        if !r.is_object() {
628                            return js_to_string_value(ctx, &r);
629                        }
630                    }
631                }
632            }
633            if ctx.pending_exception.is_some() {
634                return Err(());
635            }
636        }
637        let mut err = JSObject::new_typed(crate::object::object::ObjectType::Error);
638        if let Some(proto) = ctx.get_type_error_prototype() {
639            err.prototype = Some(proto);
640        }
641        err.set(
642            ctx.common_atoms.message,
643            JSValue::new_string(ctx.intern("Cannot convert object to primitive value")),
644        );
645        let ptr = Box::into_raw(Box::new(err)) as usize;
646        ctx.runtime_mut().gc_heap_mut().track(ptr);
647        ctx.pending_exception = Some(JSValue::new_object(ptr));
648        return Err(());
649    }
650    Ok(String::new())
651}
652
653fn build_error(
654    ctx: &mut JSContext,
655    name: &str,
656    args: &[JSValue],
657    proto: Option<*mut JSObject>,
658) -> JSValue {
659    let mut err = JSObject::new_typed(crate::object::object::ObjectType::Error);
660    set_own_ne(
661        &mut err,
662        ctx.intern("name"),
663        JSValue::new_string(ctx.intern(name)),
664    );
665    if !args.is_empty() && !args[0].is_undefined() {
666        match js_to_string_value(ctx, &args[0]) {
667            Ok(s) => {
668                set_own_ne(
669                    &mut err,
670                    ctx.intern("message"),
671                    JSValue::new_string(ctx.intern(&s)),
672                );
673            }
674            Err(()) => return JSValue::undefined(),
675        }
676    }
677    if args.len() > 1 && args[1].is_object() {
678        let cause_atom = ctx.intern("cause");
679        let opts_this = args[1];
680        let opts = opts_this.as_object();
681        let cause = get_property(ctx, opts, cause_atom, &opts_this);
682        let _ = opts;
683        if let Some(cause) = cause {
684            set_own_ne(&mut err, cause_atom, cause);
685        }
686    }
687    if let Some(p) = proto {
688        err.prototype = Some(p);
689    }
690    let ptr = Box::into_raw(Box::new(err)) as usize;
691    ctx.runtime_mut().gc_heap_mut().track(ptr);
692    JSValue::new_object(ptr)
693}
694
695pub fn error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
696    let proto = ctx.get_error_prototype().map(|p| p as *mut _);
697    build_error(ctx, "Error", args, proto)
698}
699
700pub fn type_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
701    let proto = ctx.get_type_error_prototype().map(|p| p as *mut _);
702    build_error(ctx, "TypeError", args, proto)
703}
704
705pub fn reference_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
706    let proto = ctx
707        .get_reference_error_prototype()
708        .or_else(|| ctx.get_error_prototype())
709        .map(|p| p as *mut _);
710    build_error(ctx, "ReferenceError", args, proto)
711}
712
713pub fn syntax_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
714    let proto = ctx.get_syntax_error_prototype().map(|p| p as *mut _);
715    build_error(ctx, "SyntaxError", args, proto)
716}
717
718pub fn range_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
719    let proto = ctx
720        .get_range_error_prototype()
721        .or_else(|| ctx.get_error_prototype())
722        .map(|p| p as *mut _);
723    build_error(ctx, "RangeError", args, proto)
724}
725
726pub fn uri_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
727    let proto = ctx
728        .get_uri_error_prototype()
729        .or_else(|| ctx.get_error_prototype())
730        .map(|p| p as *mut _);
731    build_error(ctx, "URIError", args, proto)
732}
733
734pub fn eval_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
735    let proto = ctx
736        .get_eval_error_prototype()
737        .or_else(|| ctx.get_error_prototype())
738        .map(|p| p as *mut _);
739    build_error(ctx, "EvalError", args, proto)
740}
741
742fn error_to_string(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
743    if args.is_empty() {
744        let mut err =
745            crate::object::object::JSObject::new_typed(crate::object::object::ObjectType::Error);
746        if let Some(proto) = ctx.get_type_error_prototype() {
747            err.prototype = Some(proto);
748        }
749        err.set(
750            ctx.common_atoms.message,
751            JSValue::new_string(ctx.intern("Error.prototype.toString called on undefined")),
752        );
753        let ptr = Box::into_raw(Box::new(err)) as usize;
754        ctx.runtime_mut().gc_heap_mut().track(ptr);
755        ctx.pending_exception = Some(JSValue::new_object(ptr));
756        return JSValue::undefined();
757    }
758    let this = &args[0];
759    if !this.is_object() {
760        let mut err =
761            crate::object::object::JSObject::new_typed(crate::object::object::ObjectType::Error);
762        if let Some(proto) = ctx.get_type_error_prototype() {
763            err.prototype = Some(proto);
764        }
765        err.set(
766            ctx.common_atoms.message,
767            JSValue::new_string(ctx.intern("Error.prototype.toString called on non-object")),
768        );
769        let ptr = Box::into_raw(Box::new(err)) as usize;
770        ctx.runtime_mut().gc_heap_mut().track(ptr);
771        ctx.pending_exception = Some(JSValue::new_object(ptr));
772        return JSValue::undefined();
773    }
774
775    let obj = this.as_object();
776    let name_atom = ctx.intern("name");
777    let name_val = get_property(ctx, &obj, name_atom, this);
778    let _ = obj;
779    if ctx.pending_exception.is_some() {
780        return JSValue::undefined();
781    }
782    let name: Result<String, ()> = match name_val {
783        Some(n) => {
784            if n.is_undefined() {
785                Ok("Error".to_string())
786            } else {
787                match js_to_string_value(ctx, &n) {
788                    Ok(s) => Ok(s),
789                    Err(()) => return JSValue::undefined(),
790                }
791            }
792        }
793        None => Ok("Error".to_string()),
794    };
795    if ctx.pending_exception.is_some() {
796        return JSValue::undefined();
797    }
798
799    let obj2 = this.as_object();
800    let msg_atom = ctx.intern("message");
801    let message_val = get_property(ctx, &obj2, msg_atom, this);
802    let _ = obj2;
803    if ctx.pending_exception.is_some() {
804        return JSValue::undefined();
805    }
806    let message: Result<String, ()> = match message_val {
807        Some(m) => {
808            if m.is_undefined() {
809                Ok(String::new())
810            } else {
811                match js_to_string_value(ctx, &m) {
812                    Ok(s) => Ok(s),
813                    Err(()) => return JSValue::undefined(),
814                }
815            }
816        }
817        None => Ok(String::new()),
818    };
819
820    match (name, message) {
821        (Ok(n), Ok(m)) => {
822            if n.is_empty() {
823                JSValue::new_string(ctx.intern(&m))
824            } else if m.is_empty() {
825                JSValue::new_string(ctx.intern(&n))
826            } else {
827                JSValue::new_string(ctx.intern(&format!("{}: {}", n, m)))
828            }
829        }
830        _ => JSValue::undefined(),
831    }
832}
833
834pub fn register_builtins(ctx: &mut JSContext) {
835    ctx.register_builtin(
836        "error_constructor",
837        HostFunction::ctor("Error", 1, error_constructor),
838    );
839    ctx.register_builtin(
840        "type_error_constructor",
841        HostFunction::ctor("TypeError", 1, type_error_constructor),
842    );
843    ctx.register_builtin(
844        "reference_error_constructor",
845        HostFunction::ctor("ReferenceError", 1, reference_error_constructor),
846    );
847    ctx.register_builtin(
848        "syntax_error_constructor",
849        HostFunction::ctor("SyntaxError", 1, syntax_error_constructor),
850    );
851    ctx.register_builtin(
852        "range_error_constructor",
853        HostFunction::ctor("RangeError", 1, range_error_constructor),
854    );
855    ctx.register_builtin(
856        "uri_error_constructor",
857        HostFunction::ctor("URIError", 1, uri_error_constructor),
858    );
859    ctx.register_builtin(
860        "eval_error_constructor",
861        HostFunction::ctor("EvalError", 1, eval_error_constructor),
862    );
863    ctx.register_builtin(
864        "error_toString",
865        HostFunction::method("toString", 0, error_to_string),
866    );
867}