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}