1use std::collections::HashMap;
24use std::rc::Rc;
25
26use crate::nan_value::{Arena, NanString, NanValue};
27use crate::value::{RuntimeError, Value, list_from_vec, list_view};
28
29pub fn register(global: &mut HashMap<String, Value>) {
30 let mut members = HashMap::new();
31 for method in &[
32 "len",
33 "byteLength",
34 "startsWith",
35 "endsWith",
36 "contains",
37 "slice",
38 "trim",
39 "split",
40 "replace",
41 "join",
42 "charAt",
43 "chars",
44 "fromInt",
45 "fromFloat",
46 "fromBool",
47 "toLower",
48 "toUpper",
49 ] {
50 members.insert(
51 method.to_string(),
52 Value::Builtin(format!("String.{}", method)),
53 );
54 }
55 global.insert(
56 "String".to_string(),
57 Value::Namespace {
58 name: "String".to_string(),
59 members,
60 },
61 );
62}
63
64pub fn effects(_name: &str) -> &'static [&'static str] {
65 &[]
66}
67
68pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
70 match name {
71 "String.len" => Some(length(args)),
72 "String.byteLength" => Some(byte_length(args)),
73 "String.startsWith" => Some(starts_with(args)),
74 "String.endsWith" => Some(ends_with(args)),
75 "String.contains" => Some(contains(args)),
76 "String.slice" => Some(slice(args)),
77 "String.trim" => Some(trim(args)),
78 "String.split" => Some(split(args)),
79 "String.replace" => Some(replace(args)),
80 "String.join" => Some(join(args)),
81 "String.charAt" => Some(char_at(args)),
82 "String.chars" => Some(chars(args)),
83 "String.fromInt" => Some(from_int(args)),
84 "String.fromFloat" => Some(from_float(args)),
85 "String.fromBool" => Some(from_bool(args)),
86 "String.toLower" => Some(to_lower(args)),
87 "String.toUpper" => Some(to_upper(args)),
88 _ => None,
89 }
90}
91
92fn length(args: &[Value]) -> Result<Value, RuntimeError> {
95 let [val] = one_arg("String.len", args)?;
96 let Value::Str(s) = val else {
97 return Err(RuntimeError::Error(
98 "String.len: argument must be a String".to_string(),
99 ));
100 };
101 Ok(Value::Int(s.chars().count() as i64))
102}
103
104fn byte_length(args: &[Value]) -> Result<Value, RuntimeError> {
105 let [val] = one_arg("String.byteLength", args)?;
106 let Value::Str(s) = val else {
107 return Err(RuntimeError::Error(
108 "String.byteLength: argument must be a String".to_string(),
109 ));
110 };
111 Ok(Value::Int(s.len() as i64))
112}
113
114fn starts_with(args: &[Value]) -> Result<Value, RuntimeError> {
115 let [a, b] = two_args("String.startsWith", args)?;
116 let (Value::Str(s), Value::Str(prefix)) = (a, b) else {
117 return Err(RuntimeError::Error(
118 "String.startsWith: both arguments must be String".to_string(),
119 ));
120 };
121 Ok(Value::Bool(s.starts_with(prefix.as_str())))
122}
123
124fn ends_with(args: &[Value]) -> Result<Value, RuntimeError> {
125 let [a, b] = two_args("String.endsWith", args)?;
126 let (Value::Str(s), Value::Str(suffix)) = (a, b) else {
127 return Err(RuntimeError::Error(
128 "String.endsWith: both arguments must be String".to_string(),
129 ));
130 };
131 Ok(Value::Bool(s.ends_with(suffix.as_str())))
132}
133
134fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
135 let [a, b] = two_args("String.contains", args)?;
136 let (Value::Str(s), Value::Str(sub)) = (a, b) else {
137 return Err(RuntimeError::Error(
138 "String.contains: both arguments must be String".to_string(),
139 ));
140 };
141 Ok(Value::Bool(s.contains(sub.as_str())))
142}
143
144fn slice(args: &[Value]) -> Result<Value, RuntimeError> {
145 if args.len() != 3 {
146 return Err(RuntimeError::Error(format!(
147 "String.slice() takes 3 arguments (s, from, to), got {}",
148 args.len()
149 )));
150 }
151 let Value::Str(s) = &args[0] else {
152 return Err(RuntimeError::Error(
153 "String.slice: first argument must be a String".to_string(),
154 ));
155 };
156 let Value::Int(from) = &args[1] else {
157 return Err(RuntimeError::Error(
158 "String.slice: second argument must be an Int".to_string(),
159 ));
160 };
161 let Value::Int(to) = &args[2] else {
162 return Err(RuntimeError::Error(
163 "String.slice: third argument must be an Int".to_string(),
164 ));
165 };
166 Ok(Value::Str(aver_rt::string_slice(s, *from, *to)))
167}
168
169fn trim(args: &[Value]) -> Result<Value, RuntimeError> {
170 let [val] = one_arg("String.trim", args)?;
171 let Value::Str(s) = val else {
172 return Err(RuntimeError::Error(
173 "String.trim: argument must be a String".to_string(),
174 ));
175 };
176 Ok(Value::Str(s.trim().to_string()))
177}
178
179fn split(args: &[Value]) -> Result<Value, RuntimeError> {
180 let [a, b] = two_args("String.split", args)?;
181 let (Value::Str(s), Value::Str(delim)) = (a, b) else {
182 return Err(RuntimeError::Error(
183 "String.split: both arguments must be String".to_string(),
184 ));
185 };
186 let parts: Vec<Value> = s
187 .split(delim.as_str())
188 .map(|p| Value::Str(p.to_string()))
189 .collect();
190 Ok(list_from_vec(parts))
191}
192
193fn replace(args: &[Value]) -> Result<Value, RuntimeError> {
194 if args.len() != 3 {
195 return Err(RuntimeError::Error(format!(
196 "String.replace() takes 3 arguments (s, old, new), got {}",
197 args.len()
198 )));
199 }
200 let (Value::Str(s), Value::Str(old), Value::Str(new)) = (&args[0], &args[1], &args[2]) else {
201 return Err(RuntimeError::Error(
202 "String.replace: all arguments must be String".to_string(),
203 ));
204 };
205 Ok(Value::Str(s.replace(old.as_str(), new.as_str())))
206}
207
208fn join(args: &[Value]) -> Result<Value, RuntimeError> {
209 let [a, b] = two_args("String.join", args)?;
210 let items = list_view(a).ok_or_else(|| {
211 RuntimeError::Error("String.join: first argument must be a List".to_string())
212 })?;
213 let Value::Str(sep) = b else {
214 return Err(RuntimeError::Error(
215 "String.join: second argument must be a String".to_string(),
216 ));
217 };
218 let strs: Result<Vec<String>, RuntimeError> = items
219 .iter()
220 .map(|v| match v {
221 Value::Str(s) => Ok(s.clone()),
222 _ => Err(RuntimeError::Error(
223 "String.join: list elements must be String".to_string(),
224 )),
225 })
226 .collect();
227 Ok(Value::Str(strs?.join(sep.as_str())))
228}
229
230fn char_at(args: &[Value]) -> Result<Value, RuntimeError> {
231 let [a, b] = two_args("String.charAt", args)?;
232 let Value::Str(s) = a else {
233 return Err(RuntimeError::Error(
234 "String.charAt: first argument must be a String".to_string(),
235 ));
236 };
237 let Value::Int(idx) = b else {
238 return Err(RuntimeError::Error(
239 "String.charAt: second argument must be an Int".to_string(),
240 ));
241 };
242 let idx = *idx as usize;
243 match s.chars().nth(idx) {
244 Some(c) => Ok(Value::Some(Box::new(Value::Str(c.to_string())))),
245 None => Ok(Value::None),
246 }
247}
248
249fn chars(args: &[Value]) -> Result<Value, RuntimeError> {
250 let [val] = one_arg("String.chars", args)?;
251 let Value::Str(s) = val else {
252 return Err(RuntimeError::Error(
253 "String.chars: argument must be a String".to_string(),
254 ));
255 };
256 let result: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
257 Ok(list_from_vec(result))
258}
259
260fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
261 let [val] = one_arg("String.fromInt", args)?;
262 let Value::Int(n) = val else {
263 return Err(RuntimeError::Error(
264 "String.fromInt: argument must be an Int".to_string(),
265 ));
266 };
267 Ok(Value::Str(format!("{}", n)))
268}
269
270fn from_float(args: &[Value]) -> Result<Value, RuntimeError> {
271 let [val] = one_arg("String.fromFloat", args)?;
272 let Value::Float(f) = val else {
273 return Err(RuntimeError::Error(
274 "String.fromFloat: argument must be a Float".to_string(),
275 ));
276 };
277 Ok(Value::Str(format!("{}", f)))
278}
279
280fn from_bool(args: &[Value]) -> Result<Value, RuntimeError> {
281 let [val] = one_arg("String.fromBool", args)?;
282 let Value::Bool(b) = val else {
283 return Err(RuntimeError::Error(
284 "String.fromBool: argument must be a Bool".to_string(),
285 ));
286 };
287 Ok(Value::Str(if *b { "true" } else { "false" }.to_string()))
288}
289
290fn to_lower(args: &[Value]) -> Result<Value, RuntimeError> {
291 let [val] = one_arg("String.toLower", args)?;
292 let Value::Str(s) = val else {
293 return Err(RuntimeError::Error(
294 "String.toLower: argument must be a String".to_string(),
295 ));
296 };
297 Ok(Value::Str(s.to_lowercase()))
298}
299
300fn to_upper(args: &[Value]) -> Result<Value, RuntimeError> {
301 let [val] = one_arg("String.toUpper", args)?;
302 let Value::Str(s) = val else {
303 return Err(RuntimeError::Error(
304 "String.toUpper: argument must be a String".to_string(),
305 ));
306 };
307 Ok(Value::Str(s.to_uppercase()))
308}
309
310fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
313 if args.len() != 1 {
314 return Err(RuntimeError::Error(format!(
315 "{}() takes 1 argument, got {}",
316 name,
317 args.len()
318 )));
319 }
320 Ok([&args[0]])
321}
322
323fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
324 if args.len() != 2 {
325 return Err(RuntimeError::Error(format!(
326 "{}() takes 2 arguments, got {}",
327 name,
328 args.len()
329 )));
330 }
331 Ok([&args[0], &args[1]])
332}
333
334pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
337 let methods = &[
338 "len",
339 "byteLength",
340 "startsWith",
341 "endsWith",
342 "contains",
343 "slice",
344 "trim",
345 "split",
346 "replace",
347 "join",
348 "charAt",
349 "chars",
350 "fromInt",
351 "fromFloat",
352 "fromBool",
353 "toLower",
354 "toUpper",
355 ];
356 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
357 for method in methods {
358 let idx = arena.push_builtin(&format!("String.{}", method));
359 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
360 }
361 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
362 name: Rc::from("String"),
363 members,
364 });
365 global.insert("String".to_string(), NanValue::new_namespace(ns_idx));
366}
367
368pub fn call_nv(
369 name: &str,
370 args: &[NanValue],
371 arena: &mut Arena,
372) -> Option<Result<NanValue, RuntimeError>> {
373 match name {
374 "String.len" => Some(length_nv(args, arena)),
375 "String.byteLength" => Some(byte_length_nv(args, arena)),
376 "String.startsWith" => Some(starts_with_nv(args, arena)),
377 "String.endsWith" => Some(ends_with_nv(args, arena)),
378 "String.contains" => Some(contains_nv(args, arena)),
379 "String.slice" => Some(slice_nv(args, arena)),
380 "String.trim" => Some(trim_nv(args, arena)),
381 "String.split" => Some(split_nv(args, arena)),
382 "String.replace" => Some(replace_nv(args, arena)),
383 "String.join" => Some(join_nv(args, arena)),
384 "String.charAt" => Some(char_at_nv(args, arena)),
385 "String.chars" => Some(chars_nv(args, arena)),
386 "String.fromInt" => Some(from_int_nv(args, arena)),
387 "String.fromFloat" => Some(from_float_nv(args, arena)),
388 "String.fromBool" => Some(from_bool_nv(args, arena)),
389 "String.toLower" => Some(to_lower_nv(args, arena)),
390 "String.toUpper" => Some(to_upper_nv(args, arena)),
391 _ => None,
392 }
393}
394
395fn nv_str(v: NanValue, arena: &Arena) -> Option<NanString<'_>> {
396 if v.is_string() {
397 Some(arena.get_string_value(v))
398 } else {
399 None
400 }
401}
402
403fn length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
404 if args.len() != 1 {
405 return Err(RuntimeError::Error(format!(
406 "String.len() takes 1 argument, got {}",
407 args.len()
408 )));
409 }
410 let s = nv_str(args[0], arena)
411 .ok_or_else(|| RuntimeError::Error("String.len: argument must be a String".to_string()))?;
412 Ok(NanValue::new_int(s.chars().count() as i64, arena))
413}
414
415fn byte_length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
416 if args.len() != 1 {
417 return Err(RuntimeError::Error(format!(
418 "String.byteLength() takes 1 argument, got {}",
419 args.len()
420 )));
421 }
422 let s = nv_str(args[0], arena).ok_or_else(|| {
423 RuntimeError::Error("String.byteLength: argument must be a String".to_string())
424 })?;
425 Ok(NanValue::new_int(s.len() as i64, arena))
426}
427
428fn starts_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
429 if args.len() != 2 {
430 return Err(RuntimeError::Error(format!(
431 "String.startsWith() takes 2 arguments, got {}",
432 args.len()
433 )));
434 }
435 if !args[0].is_string() || !args[1].is_string() {
436 return Err(RuntimeError::Error(
437 "String.startsWith: both arguments must be String".to_string(),
438 ));
439 }
440 let s = arena.get_string_value(args[0]);
441 let prefix = arena.get_string_value(args[1]);
442 let result = s.starts_with(prefix.as_str());
443 Ok(NanValue::new_bool(result))
444}
445
446fn ends_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
447 if args.len() != 2 {
448 return Err(RuntimeError::Error(format!(
449 "String.endsWith() takes 2 arguments, got {}",
450 args.len()
451 )));
452 }
453 if !args[0].is_string() || !args[1].is_string() {
454 return Err(RuntimeError::Error(
455 "String.endsWith: both arguments must be String".to_string(),
456 ));
457 }
458 let s = arena.get_string_value(args[0]);
459 let suffix = arena.get_string_value(args[1]);
460 let result = s.ends_with(suffix.as_str());
461 Ok(NanValue::new_bool(result))
462}
463
464fn contains_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
465 if args.len() != 2 {
466 return Err(RuntimeError::Error(format!(
467 "String.contains() takes 2 arguments, got {}",
468 args.len()
469 )));
470 }
471 if !args[0].is_string() || !args[1].is_string() {
472 return Err(RuntimeError::Error(
473 "String.contains: both arguments must be String".to_string(),
474 ));
475 }
476 let s = arena.get_string_value(args[0]);
477 let sub = arena.get_string_value(args[1]);
478 let result = s.contains(sub.as_str());
479 Ok(NanValue::new_bool(result))
480}
481
482fn slice_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
483 if args.len() != 3 {
484 return Err(RuntimeError::Error(format!(
485 "String.slice() takes 3 arguments (s, from, to), got {}",
486 args.len()
487 )));
488 }
489 if !args[0].is_string() {
490 return Err(RuntimeError::Error(
491 "String.slice: first argument must be a String".to_string(),
492 ));
493 }
494 if !args[1].is_int() {
495 return Err(RuntimeError::Error(
496 "String.slice: second argument must be an Int".to_string(),
497 ));
498 }
499 if !args[2].is_int() {
500 return Err(RuntimeError::Error(
501 "String.slice: third argument must be an Int".to_string(),
502 ));
503 }
504 let s = arena.get_string_value(args[0]).to_string();
505 let from = args[1].as_int(arena);
506 let to = args[2].as_int(arena);
507 let result = aver_rt::string_slice(&s, from, to);
508 Ok(NanValue::new_string_value(&result, arena))
509}
510
511fn trim_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
512 if args.len() != 1 {
513 return Err(RuntimeError::Error(format!(
514 "String.trim() takes 1 argument, got {}",
515 args.len()
516 )));
517 }
518 let s = nv_str(args[0], arena)
519 .ok_or_else(|| RuntimeError::Error("String.trim: argument must be a String".to_string()))?;
520 let result = s.trim().to_string();
521 Ok(NanValue::new_string_value(&result, arena))
522}
523
524fn split_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
525 if args.len() != 2 {
526 return Err(RuntimeError::Error(format!(
527 "String.split() takes 2 arguments, got {}",
528 args.len()
529 )));
530 }
531 if !args[0].is_string() || !args[1].is_string() {
532 return Err(RuntimeError::Error(
533 "String.split: both arguments must be String".to_string(),
534 ));
535 }
536 let s = arena.get_string_value(args[0]).to_string();
537 let delim = arena.get_string_value(args[1]).to_string();
538 let parts: Vec<NanValue> = s
539 .split(&*delim)
540 .map(|p| NanValue::new_string_value(p, arena))
541 .collect();
542 let list_idx = arena.push_list(parts);
543 Ok(NanValue::new_list(list_idx))
544}
545
546fn replace_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
547 if args.len() != 3 {
548 return Err(RuntimeError::Error(format!(
549 "String.replace() takes 3 arguments (s, old, new), got {}",
550 args.len()
551 )));
552 }
553 if !args[0].is_string() || !args[1].is_string() || !args[2].is_string() {
554 return Err(RuntimeError::Error(
555 "String.replace: all arguments must be String".to_string(),
556 ));
557 }
558 let s = arena.get_string_value(args[0]).to_string();
559 let old = arena.get_string_value(args[1]).to_string();
560 let new = arena.get_string_value(args[2]).to_string();
561 let result = s.replace(&*old, &new);
562 Ok(NanValue::new_string_value(&result, arena))
563}
564
565fn join_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
566 if args.len() != 2 {
567 return Err(RuntimeError::Error(format!(
568 "String.join() takes 2 arguments, got {}",
569 args.len()
570 )));
571 }
572 if !args[0].is_list() {
573 return Err(RuntimeError::Error(
574 "String.join: first argument must be a List".to_string(),
575 ));
576 }
577 if !args[1].is_string() {
578 return Err(RuntimeError::Error(
579 "String.join: second argument must be a String".to_string(),
580 ));
581 }
582 let items = arena.list_to_vec_value(args[0]);
583 let sep = arena.get_string_value(args[1]).to_string();
584 let mut strs: Vec<String> = Vec::with_capacity(items.len());
585 for item in &items {
586 if !item.is_string() {
587 return Err(RuntimeError::Error(
588 "String.join: list elements must be String".to_string(),
589 ));
590 }
591 strs.push(arena.get_string_value(*item).to_string());
592 }
593 let result = strs.join(&sep);
594 Ok(NanValue::new_string_value(&result, arena))
595}
596
597fn char_at_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
598 if args.len() != 2 {
599 return Err(RuntimeError::Error(format!(
600 "String.charAt() takes 2 arguments, got {}",
601 args.len()
602 )));
603 }
604 if !args[0].is_string() {
605 return Err(RuntimeError::Error(
606 "String.charAt: first argument must be a String".to_string(),
607 ));
608 }
609 if !args[1].is_int() {
610 return Err(RuntimeError::Error(
611 "String.charAt: second argument must be an Int".to_string(),
612 ));
613 }
614 let s = arena.get_string_value(args[0]);
615 let idx_val = args[1].as_int(arena) as usize;
616 match s.chars().nth(idx_val) {
617 Some(c) => {
618 let cs = c.to_string();
619 let inner = NanValue::new_string_value(&cs, arena);
620 Ok(NanValue::new_some_value(inner, arena))
621 }
622 None => Ok(NanValue::NONE),
623 }
624}
625
626fn chars_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
627 if args.len() != 1 {
628 return Err(RuntimeError::Error(format!(
629 "String.chars() takes 1 argument, got {}",
630 args.len()
631 )));
632 }
633 if !args[0].is_string() {
634 return Err(RuntimeError::Error(
635 "String.chars: argument must be a String".to_string(),
636 ));
637 }
638 let s = arena.get_string_value(args[0]).to_string();
639 let items: Vec<NanValue> = s
640 .chars()
641 .map(|c| NanValue::new_string_value(&c.to_string(), arena))
642 .collect();
643 let list_idx = arena.push_list(items);
644 Ok(NanValue::new_list(list_idx))
645}
646
647fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
648 if args.len() != 1 {
649 return Err(RuntimeError::Error(format!(
650 "String.fromInt() takes 1 argument, got {}",
651 args.len()
652 )));
653 }
654 if !args[0].is_int() {
655 return Err(RuntimeError::Error(
656 "String.fromInt: argument must be an Int".to_string(),
657 ));
658 }
659 let s = format!("{}", args[0].as_int(arena));
660 Ok(NanValue::new_string_value(&s, arena))
661}
662
663fn from_float_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
664 if args.len() != 1 {
665 return Err(RuntimeError::Error(format!(
666 "String.fromFloat() takes 1 argument, got {}",
667 args.len()
668 )));
669 }
670 if !args[0].is_float() {
671 return Err(RuntimeError::Error(
672 "String.fromFloat: argument must be a Float".to_string(),
673 ));
674 }
675 let s = format!("{}", args[0].as_float());
676 Ok(NanValue::new_string_value(&s, arena))
677}
678
679fn from_bool_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
680 if args.len() != 1 {
681 return Err(RuntimeError::Error(format!(
682 "String.fromBool() takes 1 argument, got {}",
683 args.len()
684 )));
685 }
686 if !args[0].is_bool() {
687 return Err(RuntimeError::Error(
688 "String.fromBool: argument must be a Bool".to_string(),
689 ));
690 }
691 let s = if args[0].as_bool() { "true" } else { "false" };
692 Ok(NanValue::new_string_value(s, arena))
693}
694
695fn to_lower_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
696 if args.len() != 1 {
697 return Err(RuntimeError::Error(format!(
698 "String.toLower() takes 1 argument, got {}",
699 args.len()
700 )));
701 }
702 if !args[0].is_string() {
703 return Err(RuntimeError::Error(
704 "String.toLower: argument must be a String".to_string(),
705 ));
706 }
707 let s = arena.get_string_value(args[0]).to_lowercase();
708 Ok(NanValue::new_string_value(&s, arena))
709}
710
711fn to_upper_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
712 if args.len() != 1 {
713 return Err(RuntimeError::Error(format!(
714 "String.toUpper() takes 1 argument, got {}",
715 args.len()
716 )));
717 }
718 if !args[0].is_string() {
719 return Err(RuntimeError::Error(
720 "String.toUpper: argument must be a String".to_string(),
721 ));
722 }
723 let s = arena.get_string_value(args[0]).to_uppercase();
724 Ok(NanValue::new_string_value(&s, arena))
725}