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 create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
7    let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern(name), 1);
8    func.set_builtin_marker(ctx, name);
9    let ptr = Box::into_raw(Box::new(func)) as usize;
10    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
11    JSValue::new_function(ptr)
12}
13
14pub fn init_error(ctx: &mut JSContext) {
15    let error_atom = ctx.intern("Error");
16
17    let mut error_func = crate::object::function::JSFunction::new_builtin(error_atom, 1);
18    error_func.set_builtin_marker(ctx, "error_constructor");
19    let error_ptr = Box::into_raw(Box::new(error_func)) as usize;
20    ctx.runtime_mut().gc_heap_mut().track_function(error_ptr);
21    let error_value = JSValue::new_function(error_ptr);
22
23    let global = ctx.global();
24    if global.is_object() {
25        let global_obj = global.as_object_mut();
26        global_obj.set(error_atom, error_value);
27    }
28
29    let mut proto_obj = JSObject::new();
30    proto_obj.set(ctx.intern("name"), JSValue::new_string(ctx.intern("Error")));
31    proto_obj.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
32    proto_obj.set(
33        ctx.intern("toString"),
34        create_builtin_function(ctx, "error_toString"),
35    );
36
37    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
38        proto_obj.prototype = Some(obj_proto_ptr);
39    }
40
41    proto_obj.set(ctx.intern("constructor"), error_value);
42
43    let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
44    ctx.set_error_prototype(proto_ptr);
45    let proto_value = JSValue::new_object(proto_ptr);
46    if ctx.get_builtin_func("error_constructor").is_some() {
47        let f = unsafe { JSValue::function_from_ptr_mut(error_ptr) };
48        f.base.set(ctx.intern("prototype"), proto_value);
49    }
50
51    let proto_atom = ctx.intern("ErrorPrototype");
52    if global.is_object() {
53        let global_obj = global.as_object_mut();
54        global_obj.set(proto_atom, proto_value);
55    }
56
57    init_type_error(ctx);
58    init_reference_error(ctx);
59    init_syntax_error(ctx);
60    init_range_error(ctx);
61}
62
63fn init_type_error(ctx: &mut JSContext) {
64    let atom = ctx.intern("TypeError");
65    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
66    ctor.set_builtin_marker(ctx, "type_error_constructor");
67    let ptr = Box::into_raw(Box::new(ctor)) as usize;
68    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
69    let value = JSValue::new_function(ptr);
70
71    let global = ctx.global();
72    if global.is_object() {
73        let global_obj = global.as_object_mut();
74        global_obj.set(atom, value);
75    }
76
77    let mut proto = JSObject::new();
78    proto.set(
79        ctx.intern("name"),
80        JSValue::new_string(ctx.intern("TypeError")),
81    );
82    proto.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
83
84    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
85        proto.prototype = Some(error_proto_ptr);
86    }
87    proto.set(ctx.intern("constructor"), value);
88
89    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
90    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
91    let proto_value = JSValue::new_object(proto_ptr);
92    ctx.set_type_error_prototype(proto_ptr);
93    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
94    ctor.base.set(ctx.intern("prototype"), proto_value);
95}
96
97fn init_reference_error(ctx: &mut JSContext) {
98    let atom = ctx.intern("ReferenceError");
99    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
100    ctor.set_builtin_marker(ctx, "reference_error_constructor");
101    let ptr = Box::into_raw(Box::new(ctor)) as usize;
102    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
103    let value = JSValue::new_function(ptr);
104
105    let mut proto = JSObject::new();
106    proto.set(
107        ctx.intern("name"),
108        JSValue::new_string(ctx.intern("ReferenceError")),
109    );
110    proto.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
111    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
112        proto.prototype = Some(error_proto_ptr);
113    }
114    proto.set(ctx.intern("constructor"), value);
115
116    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
117    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
118    let proto_value = JSValue::new_object(proto_ptr);
119    ctx.set_reference_error_prototype(proto_ptr);
120    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
121    ctor.base.set(ctx.intern("prototype"), proto_value);
122
123    let global = ctx.global();
124    if global.is_object() {
125        let global_obj = global.as_object_mut();
126        global_obj.set(atom, value);
127    }
128}
129
130fn init_syntax_error(ctx: &mut JSContext) {
131    let atom = ctx.intern("SyntaxError");
132    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
133    ctor.set_builtin_marker(ctx, "syntax_error_constructor");
134    let ptr = Box::into_raw(Box::new(ctor)) as usize;
135    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
136    let value = JSValue::new_function(ptr);
137
138    let mut proto = JSObject::new();
139    proto.set(
140        ctx.intern("name"),
141        JSValue::new_string(ctx.intern("SyntaxError")),
142    );
143    proto.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
144    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
145        proto.prototype = Some(error_proto_ptr);
146    }
147    proto.set(ctx.intern("constructor"), value);
148
149    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
150    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
151    let proto_value = JSValue::new_object(proto_ptr);
152    ctx.set_syntax_error_prototype(proto_ptr);
153    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
154    ctor.base.set(ctx.intern("prototype"), proto_value);
155
156    let global = ctx.global();
157    if global.is_object() {
158        let global_obj = global.as_object_mut();
159        global_obj.set(atom, value);
160    }
161}
162
163fn init_range_error(ctx: &mut JSContext) {
164    let atom = ctx.intern("RangeError");
165    let mut ctor = crate::object::function::JSFunction::new_builtin(atom, 1);
166    ctor.set_builtin_marker(ctx, "range_error_constructor");
167    let ptr = Box::into_raw(Box::new(ctor)) as usize;
168    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
169    let value = JSValue::new_function(ptr);
170
171    let mut proto = JSObject::new();
172    proto.set(
173        ctx.intern("name"),
174        JSValue::new_string(ctx.intern("RangeError")),
175    );
176    proto.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
177    if let Some(error_proto_ptr) = ctx.get_error_prototype() {
178        proto.prototype = Some(error_proto_ptr);
179    }
180    proto.set(ctx.intern("constructor"), value);
181
182    let proto_ptr = Box::into_raw(Box::new(proto)) as usize;
183    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
184    let proto_value = JSValue::new_object(proto_ptr);
185    ctx.set_range_error_prototype(proto_ptr);
186    let ctor = unsafe { JSValue::function_from_ptr_mut(ptr) };
187    ctor.base.set(ctx.intern("prototype"), proto_value);
188
189    let global = ctx.global();
190    if global.is_object() {
191        let global_obj = global.as_object_mut();
192        global_obj.set(atom, value);
193    }
194}
195
196pub fn error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
197    let mut err = JSObject::new();
198    err.set(ctx.intern("name"), JSValue::new_string(ctx.intern("Error")));
199
200    if !args.is_empty() && args[0].is_string() {
201        err.set(ctx.intern("message"), args[0]);
202    } else {
203        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
204    }
205
206    if args.len() > 1 && args[1].is_object() {
207        let opts = args[1].as_object();
208        if let Some(cause) = opts.get(ctx.intern("cause")) {
209            err.set(ctx.intern("cause"), cause);
210        }
211    }
212
213    if let Some(proto) = ctx.get_error_prototype() {
214        err.prototype = Some(proto as *mut _);
215    }
216
217    let ptr = Box::into_raw(Box::new(err)) as usize;
218    ctx.runtime_mut().gc_heap_mut().track(ptr);
219    JSValue::new_object(ptr)
220}
221
222pub fn type_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
223    let mut err = JSObject::new();
224    err.set(
225        ctx.intern("name"),
226        JSValue::new_string(ctx.intern("TypeError")),
227    );
228
229    if !args.is_empty() && args[0].is_string() {
230        err.set(ctx.intern("message"), args[0]);
231    } else {
232        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
233    }
234
235    if args.len() > 1 && args[1].is_object() {
236        let opts = args[1].as_object();
237        if let Some(cause) = opts.get(ctx.intern("cause")) {
238            err.set(ctx.intern("cause"), cause);
239        }
240    }
241
242    if let Some(proto) = ctx.get_type_error_prototype() {
243        err.prototype = Some(proto as *mut _);
244    }
245
246    let ptr = Box::into_raw(Box::new(err)) as usize;
247    ctx.runtime_mut().gc_heap_mut().track(ptr);
248    JSValue::new_object(ptr)
249}
250
251pub fn reference_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
252    let mut err = JSObject::new();
253    err.set(
254        ctx.intern("name"),
255        JSValue::new_string(ctx.intern("ReferenceError")),
256    );
257
258    if !args.is_empty() && args[0].is_string() {
259        err.set(ctx.intern("message"), args[0]);
260    } else {
261        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
262    }
263
264    if args.len() > 1 && args[1].is_object() {
265        let opts = args[1].as_object();
266        if let Some(cause) = opts.get(ctx.intern("cause")) {
267            err.set(ctx.intern("cause"), cause);
268        }
269    }
270
271    if let Some(proto) = ctx.get_reference_error_prototype() {
272        err.prototype = Some(proto as *mut _);
273    } else if let Some(proto) = ctx.get_error_prototype() {
274        err.prototype = Some(proto as *mut _);
275    }
276
277    let ptr = Box::into_raw(Box::new(err)) as usize;
278    ctx.runtime_mut().gc_heap_mut().track(ptr);
279    JSValue::new_object(ptr)
280}
281
282pub fn syntax_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
283    let mut err = JSObject::new();
284    err.set(
285        ctx.intern("name"),
286        JSValue::new_string(ctx.intern("SyntaxError")),
287    );
288
289    if !args.is_empty() && args[0].is_string() {
290        err.set(ctx.intern("message"), args[0]);
291    } else {
292        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
293    }
294
295    if args.len() > 1 && args[1].is_object() {
296        let opts = args[1].as_object();
297        if let Some(cause) = opts.get(ctx.intern("cause")) {
298            err.set(ctx.intern("cause"), cause);
299        }
300    }
301
302    if let Some(proto) = ctx.get_syntax_error_prototype() {
303        err.prototype = Some(proto as *mut _);
304    }
305
306    let ptr = Box::into_raw(Box::new(err)) as usize;
307    ctx.runtime_mut().gc_heap_mut().track(ptr);
308    JSValue::new_object(ptr)
309}
310
311pub fn range_error_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
312    let mut err = JSObject::new();
313    err.set(
314        ctx.intern("name"),
315        JSValue::new_string(ctx.intern("RangeError")),
316    );
317
318    if !args.is_empty() && args[0].is_string() {
319        err.set(ctx.intern("message"), args[0]);
320    } else {
321        err.set(ctx.intern("message"), JSValue::new_string(ctx.intern("")));
322    }
323
324    if args.len() > 1 && args[1].is_object() {
325        let opts = args[1].as_object();
326        if let Some(cause) = opts.get(ctx.intern("cause")) {
327            err.set(ctx.intern("cause"), cause);
328        }
329    }
330
331    if let Some(proto) = ctx.get_range_error_prototype() {
332        err.prototype = Some(proto as *mut _);
333    } else if let Some(proto) = ctx.get_error_prototype() {
334        err.prototype = Some(proto as *mut _);
335    }
336
337    let ptr = Box::into_raw(Box::new(err)) as usize;
338    ctx.runtime_mut().gc_heap_mut().track(ptr);
339    JSValue::new_object(ptr)
340}
341
342fn error_to_string(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
343    if args.is_empty() {
344        return JSValue::new_string(ctx.intern("Error"));
345    }
346    let this = &args[0];
347    if !this.is_object() {
348        return JSValue::new_string(ctx.intern("Error"));
349    }
350
351    let obj = this.as_object();
352
353    let name = if let Some(n) = obj.get(ctx.intern("name")) {
354        if n.is_string() {
355            ctx.get_atom_str(n.get_atom()).to_string()
356        } else {
357            "Error".to_string()
358        }
359    } else {
360        "Error".to_string()
361    };
362
363    let message = if let Some(m) = obj.get(ctx.intern("message")) {
364        if m.is_string() {
365            ctx.get_atom_str(m.get_atom()).to_string()
366        } else {
367            String::new()
368        }
369    } else {
370        String::new()
371    };
372
373    if message.is_empty() {
374        JSValue::new_string(ctx.intern(&name))
375    } else {
376        JSValue::new_string(ctx.intern(&format!("{}: {}", name, message)))
377    }
378}
379
380pub fn register_builtins(ctx: &mut JSContext) {
381    ctx.register_builtin(
382        "error_constructor",
383        HostFunction::new("Error", 1, error_constructor),
384    );
385    ctx.register_builtin(
386        "type_error_constructor",
387        HostFunction::new("TypeError", 1, type_error_constructor),
388    );
389    ctx.register_builtin(
390        "reference_error_constructor",
391        HostFunction::new("ReferenceError", 1, reference_error_constructor),
392    );
393    ctx.register_builtin(
394        "syntax_error_constructor",
395        HostFunction::new("SyntaxError", 1, syntax_error_constructor),
396    );
397    ctx.register_builtin(
398        "range_error_constructor",
399        HostFunction::new("RangeError", 1, range_error_constructor),
400    );
401    ctx.register_builtin(
402        "error_toString",
403        HostFunction::new("toString", 0, error_to_string),
404    );
405}