Skip to main content

pipa/builtins/
parse.rs

1use crate::runtime::context::JSContext;
2use crate::value::JSValue;
3
4pub fn global_parseint(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
5    if args.is_empty() {
6        return JSValue::new_float(f64::NAN);
7    }
8
9    let input_val = &args[0];
10    let s = if input_val.is_string() {
11        ctx.get_atom_str(input_val.get_atom()).to_string()
12    } else if input_val.is_int() {
13        input_val.get_int().to_string()
14    } else if input_val.is_float() {
15        let f = input_val.get_float();
16        if f.is_nan() || f.is_infinite() {
17            return JSValue::new_float(f64::NAN);
18        }
19        let truncated = f.trunc();
20        if truncated == 0.0 {
21            return JSValue::new_int(0);
22        }
23        format!("{}", truncated as i64)
24    } else if input_val.is_bool() {
25        if input_val.get_bool() {
26            "true".to_string()
27        } else {
28            "false".to_string()
29        }
30    } else if input_val.is_null() {
31        "null".to_string()
32    } else if input_val.is_undefined() {
33        "undefined".to_string()
34    } else if input_val.is_object() {
35        let obj = input_val.as_object();
36        let mut prim: Option<Option<String>> = None;
37        let mut threw_error = false;
38        if let Some(to_str) = obj.get(ctx.intern("toString")) {
39            if to_str.is_function() {
40                if let Some(ptr) = ctx.get_register_vm_ptr() {
41                    let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
42                    match vm.call_function_with_this(ctx, to_str, *input_val, &[]) {
43                        Ok(result) if result.is_string() => {
44                            prim = Some(Some(ctx.get_atom_str(result.get_atom()).to_string()));
45                        }
46                        Ok(result) if result.is_int() => {
47                            prim = Some(Some(result.get_int().to_string()));
48                        }
49                        Ok(result) if result.is_float() => {
50                            prim = Some(Some(result.get_float().to_string()));
51                        }
52                        Ok(_) => {
53                            prim = Some(None);
54                        }
55                        Err(_) => {
56                            threw_error = true;
57                        }
58                    }
59                }
60            }
61        }
62        if threw_error {
63            return JSValue::undefined();
64        }
65        if let Some(Some(_)) = prim {
66        } else {
67            if let Some(val_of) = obj.get(ctx.intern("valueOf")) {
68                if val_of.is_function() {
69                    if let Some(ptr) = ctx.get_register_vm_ptr() {
70                        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
71                        match vm.call_function_with_this(ctx, val_of, *input_val, &[]) {
72                            Ok(result) if result.is_string() => {
73                                prim = Some(Some(ctx.get_atom_str(result.get_atom()).to_string()));
74                            }
75                            Ok(result) if result.is_int() => {
76                                prim = Some(Some(result.get_int().to_string()));
77                            }
78                            Ok(result) if result.is_float() => {
79                                prim = Some(Some(result.get_float().to_string()));
80                            }
81                            Ok(_) => {
82                                prim = Some(None);
83                            }
84                            _ => {}
85                        }
86                    }
87                }
88            }
89        }
90        match prim {
91            Some(Some(s)) => s,
92            _ => {
93                return crate::builtins::global::throw_type_error(
94                    ctx,
95                    "Cannot convert object to primitive value",
96                );
97            }
98        }
99    } else {
100        return JSValue::new_float(f64::NAN);
101    };
102
103    let mut input = s.trim_start();
104    let mut sign = 1f64;
105    if let Some(rest) = input.strip_prefix('-') {
106        sign = -1.0;
107        input = rest;
108    } else if let Some(rest) = input.strip_prefix('+') {
109        input = rest;
110    }
111
112    let radix_arg = args.get(1);
113    let mut radix = if let Some(ra) = radix_arg {
114        let n = if ra.is_int() {
115            ra.get_int() as f64
116        } else if ra.is_float() {
117            ra.get_float()
118        } else if ra.is_bool() {
119            if ra.get_bool() { 1.0 } else { 0.0 }
120        } else if ra.is_null() {
121            0.0
122        } else if ra.is_undefined() {
123            0.0
124        } else if ra.is_string() {
125            let s = ctx.get_atom_str(ra.get_atom());
126            if s.trim().is_empty() {
127                0.0
128            } else {
129                match s.trim().parse::<f64>() {
130                    Ok(v) if v.is_nan() => 0.0,
131                    Ok(v) => v,
132                    Err(_) => 0.0,
133                }
134            }
135        } else if ra.is_object() {
136            let obj = ra.as_object();
137            let mut radix_num = None;
138            if let Some(val_of) = obj.get(ctx.intern("valueOf")) {
139                if val_of.is_function() {
140                    if let Some(ptr) = ctx.get_register_vm_ptr() {
141                        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
142                        match vm.call_function_with_this(ctx, val_of, *ra, &[]) {
143                            Ok(result) if result.is_int() => {
144                                radix_num = Some(result.get_int() as f64)
145                            }
146                            Ok(result) if result.is_float() => radix_num = Some(result.get_float()),
147                            Ok(result) if result.is_bool() => {
148                                radix_num = Some(if result.get_bool() { 1.0 } else { 0.0 });
149                            }
150                            _ => {}
151                        }
152                        if ctx.pending_exception.is_some() {
153                            return JSValue::undefined();
154                        }
155                    }
156                }
157            }
158            if radix_num.is_none() {
159                if let Some(to_str) = obj.get(ctx.intern("toString")) {
160                    if to_str.is_function() {
161                        if let Some(ptr) = ctx.get_register_vm_ptr() {
162                            let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
163                            match vm.call_function_with_this(ctx, to_str, *ra, &[]) {
164                                Ok(result) if result.is_string() => {
165                                    let s = ctx.get_atom_str(result.get_atom());
166                                    if let Ok(v) = s.trim().parse::<f64>() {
167                                        radix_num = Some(v);
168                                    }
169                                }
170                                Ok(result) if result.is_int() => {
171                                    radix_num = Some(result.get_int() as f64)
172                                }
173                                Ok(result) if result.is_float() => {
174                                    radix_num = Some(result.get_float())
175                                }
176                                _ => {}
177                            }
178                        }
179                    }
180                }
181            }
182            if ctx.pending_exception.is_some() {
183                return JSValue::undefined();
184            }
185            match radix_num {
186                Some(v) => v,
187                None => {
188                    return crate::builtins::global::throw_type_error(
189                        ctx,
190                        "Cannot convert object to primitive value",
191                    );
192                }
193            }
194        } else {
195            0.0
196        };
197        if n.is_nan() || n.is_infinite() {
198            0
199        } else {
200            let r = (n.trunc() as i64 & 0xFFFFFFFF) as i32;
201            if r == 1 {
202                return JSValue::new_float(f64::NAN);
203            }
204            r
205        }
206    } else {
207        0
208    };
209
210    if radix != 0 && !(2..=36).contains(&radix) {
211        return JSValue::new_float(f64::NAN);
212    }
213
214    if radix == 0 {
215        if input.starts_with("0x") || input.starts_with("0X") {
216            radix = 16;
217            input = &input[2..];
218        } else {
219            radix = 10;
220        }
221    } else if radix == 16 && (input.starts_with("0x") || input.starts_with("0X")) {
222        input = &input[2..];
223    }
224
225    let mut result: f64 = 0.0;
226    let mut has_digits = false;
227    for ch in input.chars() {
228        if let Some(d) = ch.to_digit(radix as u32) {
229            result = result * (radix as f64) + (d as f64);
230            has_digits = true;
231        } else {
232            break;
233        }
234    }
235
236    if !has_digits {
237        return JSValue::new_float(f64::NAN);
238    }
239
240    let final_val = sign * result;
241    if final_val >= -(1i64 << 47) as f64
242        && final_val < (1i64 << 47) as f64
243        && final_val == final_val.trunc()
244    {
245        JSValue::new_int(final_val as i64)
246    } else {
247        JSValue::new_float(final_val)
248    }
249}
250
251pub fn global_parsefloat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
252    if args.is_empty() {
253        return JSValue::new_float(f64::NAN);
254    }
255
256    let input = &args[0];
257
258    if input.is_symbol() {
259        if let Some(ptr) = ctx.get_register_vm_ptr() {
260            let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
261            let msg_atom = ctx.intern("TypeError: Cannot convert Symbol to string");
262            vm.pending_throw = Some(JSValue::new_string(msg_atom));
263        }
264        return JSValue::undefined();
265    }
266
267    if input.is_float() {
268        let f = input.get_float();
269        if f == 0.0 {
270            return JSValue::new_int(0);
271        }
272        return *input;
273    }
274    if input.is_int() {
275        return *input;
276    }
277
278    let s = if input.is_string() {
279        ctx.get_atom_str(input.get_atom()).to_string()
280    } else if input.is_object() {
281        let obj = input.as_object();
282        let mut result_str: Option<String> = None;
283        let mut threw_error = false;
284        if let Some(to_str) = obj.get(ctx.intern("toString")) {
285            if to_str.is_function() {
286                if let Some(ptr) = ctx.get_register_vm_ptr() {
287                    let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
288                    match vm.call_function_with_this(ctx, to_str, *input, &[]) {
289                        Ok(result) => {
290                            if result.is_string() {
291                                result_str = Some(ctx.get_atom_str(result.get_atom()).to_string());
292                            } else if result.is_int() {
293                                result_str = Some(result.get_int().to_string());
294                            } else if result.is_float() {
295                                result_str = Some(result.get_float().to_string());
296                            }
297                        }
298                        Err(_) => {
299                            threw_error = true;
300                        }
301                    }
302                }
303            }
304        }
305        if threw_error {
306            return JSValue::undefined();
307        }
308        if result_str.is_none() {
309            if let Some(value_of) = obj.get(ctx.intern("valueOf")) {
310                if value_of.is_function() {
311                    if let Some(ptr) = ctx.get_register_vm_ptr() {
312                        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
313                        match vm.call_function_with_this(ctx, value_of, *input, &[]) {
314                            Ok(result) => {
315                                if result.is_string() {
316                                    result_str =
317                                        Some(ctx.get_atom_str(result.get_atom()).to_string());
318                                } else if result.is_int() {
319                                    result_str = Some(result.get_int().to_string());
320                                } else if result.is_float() {
321                                    result_str = Some(result.get_float().to_string());
322                                }
323                            }
324                            _ => {}
325                        }
326                    }
327                }
328            }
329        }
330        match result_str {
331            Some(s) => s,
332            None => {
333                if let Some(ptr) = ctx.get_register_vm_ptr() {
334                    let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
335                    let mut err = crate::object::object::JSObject::new_typed(
336                        crate::object::object::ObjectType::Error,
337                    );
338                    err.set(
339                        ctx.common_atoms.message,
340                        JSValue::new_string(ctx.intern("Cannot convert object to primitive value")),
341                    );
342                    err.set(
343                        ctx.common_atoms.name,
344                        JSValue::new_string(ctx.intern("TypeError")),
345                    );
346                    if let Some(proto) = ctx.get_type_error_prototype() {
347                        err.prototype = Some(proto);
348                    }
349                    let err_ptr = Box::into_raw(Box::new(err)) as usize;
350                    ctx.runtime_mut().gc_heap_mut().track(err_ptr);
351                    vm.pending_throw = Some(JSValue::new_object(err_ptr));
352                }
353                return JSValue::undefined();
354            }
355        }
356    } else {
357        return JSValue::new_float(f64::NAN);
358    };
359
360    let trimmed = s.trim_start();
361
362    if trimmed.is_empty() {
363        return JSValue::new_float(f64::NAN);
364    }
365
366    let bytes = trimmed.as_bytes();
367    let mut pos = 0;
368
369    if pos < bytes.len() && (bytes[pos] == b'+' || bytes[pos] == b'-') {
370        pos += 1;
371    }
372
373    let start_num = pos;
374
375    while pos < bytes.len() && bytes[pos].is_ascii_digit() {
376        pos += 1;
377    }
378
379    if pos < bytes.len() && bytes[pos] == b'.' {
380        pos += 1;
381        while pos < bytes.len() && bytes[pos].is_ascii_digit() {
382            pos += 1;
383        }
384    }
385
386    if pos < bytes.len() && (bytes[pos] == b'e' || bytes[pos] == b'E') {
387        let e_pos = pos;
388        pos += 1;
389        if pos < bytes.len() && (bytes[pos] == b'+' || bytes[pos] == b'-') {
390            pos += 1;
391        }
392        if pos < bytes.len() && bytes[pos].is_ascii_digit() {
393            while pos < bytes.len() && bytes[pos].is_ascii_digit() {
394                pos += 1;
395            }
396        } else {
397            pos = e_pos;
398        }
399    }
400
401    if pos == start_num {
402        let rest = trimmed;
403        if rest.starts_with("Infinity") || rest.starts_with("+Infinity") {
404            return JSValue::new_float(f64::INFINITY);
405        }
406        if rest.starts_with("-Infinity") {
407            return JSValue::new_float(f64::NEG_INFINITY);
408        }
409        return JSValue::new_float(f64::NAN);
410    }
411
412    let num_str = &trimmed[..pos];
413
414    if num_str == "Infinity" || num_str == "+Infinity" {
415        return JSValue::new_float(f64::INFINITY);
416    }
417    if num_str == "-Infinity" {
418        return JSValue::new_float(f64::NEG_INFINITY);
419    }
420
421    match num_str.parse::<f64>() {
422        Ok(v) if v == 0.0 => JSValue::new_int(0),
423        Ok(v) => JSValue::new_float(v),
424        Err(_) => JSValue::new_float(f64::NAN),
425    }
426}