1use crate::runtime::host::{abi_arg, abi_result};
7use wasmtime::{
8 ArrayRef, ArrayRefPre, ArrayType, Caller, FuncType, HeapType, Linker, RefType, Rooted,
9 StructRef, StructRefPre, StructType, Val, ValType,
10};
11
12use super::gc_text::{GcText, GcUnits, Run, allocate, fill_run};
13use super::transforms::{TrimEnds, to_lower_case, to_upper_case, trim_range};
14use super::{
15 RangeError, Str, at_index, cmp, code_point, concat_length, ends_with_window, from_char_code,
16 from_code_point, includes, index_of, is_well_formed, last_index_of, normalize, pad_length,
17 repeat_length, slice_range, starts_with_window, string_index_unit, substr_range,
18 substring_range, to_well_formed, unit_index,
19};
20use crate::runtime::StoreData;
21use crate::runtime::fuel;
22use crate::runtime::host::{
23 fatal_host_error, intrinsic_string_type, range_error, read_code_unit, read_code_units,
24 read_code_units_range, register_host_fn, register_host_fn_async, string_array_type,
25 write_boxed_number_struct, write_submilli_string_struct, write_submilli_string_struct_units,
26};
27use crate::runtime::intrinsic_types::{build_intrinsic_types, intrinsic_types};
28use crate::runtime::prelude::{MODULE_NAME, declare_method};
29use crate::{MangledName, PackageDeclaration, Param, Type};
30
31fn method_key(method: &str) -> MangledName {
35 crate::mangle::extend(&crate::mangle::prelude("String"), method)
36}
37
38fn ctor_key(method: &str) -> MangledName {
42 crate::mangle::extend(&crate::mangle::prelude("StringConstructor"), method)
43}
44
45pub fn install(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
46 let engine = linker.engine().clone();
47 let abi = StringAbi::recover(&engine)?;
48
49 reg_unit_to_str(linker, &engine, &abi, "charAt", unit_index)?;
52 reg_string_index(linker, &engine, &abi)?;
53 reg_unit_to_str_or_undefined(linker, &engine, &abi, "at", at_index)?;
54 reg_char_code_at(linker, &engine, &abi)?;
55 reg_code_point_at(linker, &engine, &abi)?;
56
57 reg_range_to_str(linker, &engine, &abi, "slice", slice_range)?;
58 reg_range_to_str(linker, &engine, &abi, "substring", substring_range)?;
59 reg_range_to_str(linker, &engine, &abi, "substr", substr_range)?;
60
61 reg_str_str_num_to_num(linker, &engine, &abi, "indexOf", index_of)?;
62 reg_str_str_num_to_num(linker, &engine, &abi, "lastIndexOf", last_index_of)?;
63 register_string_search_predicate(linker, &engine, &abi, "includes", includes)?;
64 reg_window_predicate(linker, &engine, &abi, "startsWith", starts_with_window)?;
65 reg_window_predicate(linker, &engine, &abi, "endsWith", ends_with_window)?;
66
67 reg_concat(linker, &engine, &abi)?;
68 reg_pad(linker, &engine, &abi, "padStart", PadSide::Start)?;
69 reg_pad(linker, &engine, &abi, "padEnd", PadSide::End)?;
70
71 reg_str_to_bool(linker, &engine, &abi, "isWellFormed", is_well_formed)?;
72 reg_str_to_str(linker, &engine, &abi, "toWellFormed", to_well_formed)?;
73
74 super::transforms::prepare_case_properties()?;
75 reg_case_map(linker, &engine, &abi, "toUpperCase", |text| {
78 to_upper_case(text)
79 })?;
80 reg_case_map(linker, &engine, &abi, "toLowerCase", |text| {
81 to_lower_case(text)
82 })?;
83 reg_trim(linker, &engine, &abi, "trim", TrimEnds::Both)?;
84 reg_trim(linker, &engine, &abi, "trimStart", TrimEnds::Start)?;
85 reg_trim(linker, &engine, &abi, "trimEnd", TrimEnds::End)?;
86 reg_trim(linker, &engine, &abi, "trimLeft", TrimEnds::Start)?;
87 reg_trim(linker, &engine, &abi, "trimRight", TrimEnds::End)?;
88 reg_str_str_to_str_fallible(linker, &engine, &abi, "normalize", normalize)?;
89
90 reg_repeat(linker, &engine, &abi)?;
91
92 let array_ref = ValType::Ref(RefType::new(
95 false,
96 HeapType::ConcreteStruct(build_intrinsic_types(&engine)?.array),
97 ));
98 register_host_fn(
99 linker,
100 MODULE_NAME,
101 ctor_key("fromCharCode"),
102 FuncType::new(&engine, [array_ref.clone()], [abi.value_type()]),
103 move |caller, params, results| {
104 let codes = read_number_array(caller, abi_arg(params, 0)?, "String.fromCharCode")?;
105 let out = from_char_code(&codes);
106 let st = write_submilli_string_struct_units(caller, out.units())?;
107 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
108 Ok(())
109 },
110 )?;
111 register_host_fn(
112 linker,
113 MODULE_NAME,
114 ctor_key("fromCodePoint"),
115 FuncType::new(&engine, [array_ref], [abi.value_type()]),
116 move |caller, params, results| {
117 let codes = read_number_array(caller, abi_arg(params, 0)?, "String.fromCodePoint")?;
118 let out = from_code_point(&codes).map_err(throw)?;
119 let st = write_submilli_string_struct_units(caller, out.units())?;
120 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
121 Ok(())
122 },
123 )?;
124
125 register_host_fn(
128 linker,
129 MODULE_NAME,
130 method_key("toString"),
131 FuncType::new(&engine, [abi.value_type()], [abi.value_type()]),
132 |_caller, params, results| {
133 *abi_result(results, 0)? = *abi_arg(params, 0)?;
134 Ok(())
135 },
136 )?;
137 let json_abi = abi.clone();
138 register_host_fn(
139 linker,
140 MODULE_NAME,
141 method_key("toJson"),
142 FuncType::new(&engine, [abi.value_type()], [abi.value_type()]),
143 move |caller, params, results| {
144 let recv = json_abi.read(caller, abi_arg(params, 0)?, "String#toJson")?;
145 *abi_result(results, 0)? =
146 crate::runtime::prelude::vtable::quote_string(caller, recv.value.units())?;
147 Ok(())
148 },
149 )?;
150 let equals_abi = abi.clone();
151 register_host_fn(
152 linker,
153 MODULE_NAME,
154 method_key("equals"),
155 FuncType::new(
156 &engine,
157 [abi.value_type(), abi.value_type()],
158 [ValType::I32],
159 ),
160 move |caller, params, results| {
161 *abi_result(results, 0)? = Val::I32(super::super::vtable::string_equals(
162 caller,
163 abi_arg(params, 0)?,
164 abi_arg(params, 1)?,
165 &equals_abi.string_ty,
166 )? as i32);
167 Ok(())
168 },
169 )?;
170 let lc_abi = abi.clone();
171 register_host_fn(
172 linker,
173 MODULE_NAME,
174 method_key("localeCompare"),
175 FuncType::new(
176 &engine,
177 [abi.value_type(), abi.value_type()],
178 [ValType::F64],
179 ),
180 move |caller, params, results| {
181 let a = lc_abi.read(caller, abi_arg(params, 0)?, "String#localeCompare")?;
182 let b = lc_abi.read(caller, abi_arg(params, 1)?, "String#localeCompare")?;
183 *abi_result(results, 0)? = Val::F64(f64::from(cmp(&a.value, &b.value)).to_bits());
184 Ok(())
185 },
186 )?;
187
188 let obj_param = ValType::Ref(RefType::new(
193 true,
194 HeapType::ConcreteStruct(build_intrinsic_types(&engine)?.object),
195 ));
196 register_host_fn_async(
197 linker,
198 MODULE_NAME,
199 crate::mangle::extend(&crate::mangle::prelude("StringConstructor"), "@call"),
200 FuncType::new(&engine, [obj_param], [abi.value_type()]),
201 |caller, params, results| {
202 Box::pin(async move {
203 *abi_result(results, 0)? = string_ctor_call(caller, abi_arg(params, 0)?).await?;
204 Ok(())
205 })
206 },
207 )?;
208
209 let obj_ret = ValType::Ref(RefType::new(
211 true,
212 HeapType::ConcreteStruct(build_intrinsic_types(&engine)?.object),
213 ));
214 register_host_fn(
215 linker,
216 MODULE_NAME,
217 method_key("iterator"),
218 FuncType::new(&engine, [abi.value_type()], [obj_ret]),
219 |caller, params, results| {
220 *abi_result(results, 0)? = crate::runtime::prelude::iterator::make_string_iterator(
221 caller,
222 *abi_arg(params, 0)?,
223 )?;
224 Ok(())
225 },
226 )?;
227
228 let s = abi.value_type();
231 let concat_abi = abi.clone();
232 register_host_fn(
233 linker,
234 MODULE_NAME,
235 crate::mangle::prelude("string_concat"),
236 FuncType::new(&engine, [s.clone(), s.clone()], [s.clone()]),
237 move |caller, params, results| {
238 *abi_result(results, 0)? = concat_abi.concat(
239 caller,
240 abi_arg(params, 0)?,
241 abi_arg(params, 1)?,
242 "string_concat",
243 )?;
244 Ok(())
245 },
246 )?;
247 let eq_abi = abi.clone();
248 register_host_fn(
249 linker,
250 MODULE_NAME,
251 crate::mangle::prelude("string_eq"),
252 FuncType::new(&engine, [s.clone(), s.clone()], [ValType::I32]),
253 move |caller, params, results| {
254 let equal = super::super::vtable::string_equals(
255 caller,
256 abi_arg(params, 0)?,
257 abi_arg(params, 1)?,
258 &eq_abi.string_ty,
259 )?;
260 *abi_result(results, 0)? = Val::I32(equal as i32);
261 Ok(())
262 },
263 )?;
264 let cmp_abi = abi.clone();
265 register_host_fn(
266 linker,
267 MODULE_NAME,
268 crate::mangle::prelude("string_cmp"),
269 FuncType::new(&engine, [s.clone(), s], [ValType::I32]),
270 move |caller, params, results| {
271 let a = cmp_abi.read(caller, abi_arg(params, 0)?, "string_cmp")?;
272 let b = cmp_abi.read(caller, abi_arg(params, 1)?, "string_cmp")?;
273 *abi_result(results, 0)? = Val::I32(cmp(&a.value, &b.value));
274 Ok(())
275 },
276 )?;
277 Ok(())
278}
279
280pub fn declare(defs: &mut PackageDeclaration) {
281 let m = |defs: &mut PackageDeclaration, name: &str, params: Vec<Param>, ret: Type| {
282 declare_method(defs, name, method_key(name), params, ret);
283 };
284 let s = || Param::new("s", Type::String);
285
286 m(
287 defs,
288 "charAt",
289 vec![s(), Param::new("index", Type::Number)],
290 Type::String,
291 );
292 m(
293 defs,
294 "at",
295 vec![s(), Param::new("index", Type::Number)],
296 Type::Union(vec![Type::String, Type::Undefined]),
297 );
298 m(
299 defs,
300 "charCodeAt",
301 vec![s(), Param::new("index", Type::Number)],
302 Type::Number,
303 );
304 m(
305 defs,
306 "codePointAt",
307 vec![s(), Param::new("index", Type::Number)],
308 Type::Union(vec![Type::Number, Type::Undefined]),
309 );
310 m(
311 defs,
312 "slice",
313 vec![
314 s(),
315 Param::new("start", Type::Number),
316 Param::new("end", Type::Number),
317 ],
318 Type::String,
319 );
320 m(
321 defs,
322 "substring",
323 vec![
324 s(),
325 Param::new("start", Type::Number),
326 Param::new("end", Type::Number),
327 ],
328 Type::String,
329 );
330 m(
331 defs,
332 "substr",
333 vec![
334 s(),
335 Param::new("start", Type::Number),
336 Param::new("length", Type::Number),
337 ],
338 Type::String,
339 );
340 m(
341 defs,
342 "indexOf",
343 vec![
344 s(),
345 Param::new("search", Type::String),
346 Param::new("fromIndex", Type::Number),
347 ],
348 Type::Number,
349 );
350 m(
351 defs,
352 "lastIndexOf",
353 vec![
354 s(),
355 Param::new("search", Type::String),
356 Param::new("fromIndex", Type::Number),
357 ],
358 Type::Number,
359 );
360 m(
361 defs,
362 "includes",
363 vec![
364 s(),
365 Param::new("search", Type::Unknown),
366 Param::new("fromIndex", Type::Unknown),
367 ],
368 Type::Boolean,
369 );
370 m(
371 defs,
372 "startsWith",
373 vec![
374 s(),
375 Param::new("search", Type::Unknown),
376 Param::new("position", Type::Unknown),
377 ],
378 Type::Boolean,
379 );
380 m(
381 defs,
382 "endsWith",
383 vec![
384 s(),
385 Param::new("search", Type::Unknown),
386 Param::new("endPosition", Type::Unknown),
387 ],
388 Type::Boolean,
389 );
390 m(
391 defs,
392 "concat",
393 vec![
394 s(),
395 Param::new("strings", Type::Array(Box::new(Type::String))),
396 ],
397 Type::String,
398 );
399 m(
400 defs,
401 "padStart",
402 vec![
403 s(),
404 Param::new("targetLength", Type::Number),
405 Param::new("padString", Type::String),
406 ],
407 Type::String,
408 );
409 m(
410 defs,
411 "padEnd",
412 vec![
413 s(),
414 Param::new("targetLength", Type::Number),
415 Param::new("padString", Type::String),
416 ],
417 Type::String,
418 );
419 m(defs, "isWellFormed", vec![s()], Type::Boolean);
420 m(defs, "toWellFormed", vec![s()], Type::String);
421 m(defs, "toUpperCase", vec![s()], Type::String);
422 m(defs, "toLowerCase", vec![s()], Type::String);
423 m(defs, "trim", vec![s()], Type::String);
424 m(defs, "trimStart", vec![s()], Type::String);
425 m(defs, "trimEnd", vec![s()], Type::String);
426 m(defs, "trimLeft", vec![s()], Type::String);
427 m(defs, "trimRight", vec![s()], Type::String);
428 m(
429 defs,
430 "normalize",
431 vec![s(), Param::new("form", Type::String)],
432 Type::String,
433 );
434 m(
435 defs,
436 "repeat",
437 vec![s(), Param::new("count", Type::Number)],
438 Type::String,
439 );
440
441 declare_method(
442 defs,
443 "fromCharCode",
444 ctor_key("fromCharCode"),
445 vec![Param::rest("codes", Type::Array(Box::new(Type::Number)))],
446 Type::String,
447 );
448 declare_method(
449 defs,
450 "fromCodePoint",
451 ctor_key("fromCodePoint"),
452 vec![Param::rest(
453 "codePoints",
454 Type::Array(Box::new(Type::Number)),
455 )],
456 Type::String,
457 );
458
459 m(
460 defs,
461 "iterator",
462 vec![s()],
463 Type::prelude_interface("Iterator".to_string(), vec![Type::String]),
464 );
465 m(defs, "toString", vec![s()], Type::String);
466 m(defs, "toJson", vec![s()], Type::String);
467 m(
468 defs,
469 "equals",
470 vec![s(), Param::new("other", Type::String)],
471 Type::Boolean,
472 );
473 m(
474 defs,
475 "localeCompare",
476 vec![s(), Param::new("other", Type::String)],
477 Type::Number,
478 );
479 declare_method(
480 defs,
481 "@call",
482 crate::mangle::extend(&crate::mangle::prelude("StringConstructor"), "@call"),
483 vec![Param::new("value", Type::Unknown)],
484 Type::String,
485 );
486
487 let two_strings = || vec![Param::anon(Type::String), Param::anon(Type::String)];
490 declare_method(
491 defs,
492 "string_concat",
493 crate::mangle::prelude("string_concat"),
494 two_strings(),
495 Type::String,
496 );
497 declare_method(
498 defs,
499 "string_eq",
500 crate::mangle::prelude("string_eq"),
501 two_strings(),
502 Type::Boolean,
503 );
504 declare_method(
505 defs,
506 "string_cmp",
507 crate::mangle::prelude("string_cmp"),
508 two_strings(),
509 Type::Boolean,
510 );
511 declare_method(
512 defs,
513 "string_index",
514 crate::mangle::prelude("string_index"),
515 vec![Param::anon(Type::String), Param::anon(Type::Number)],
516 Type::String,
517 );
518}
519
520fn reg_unit_to_str(
523 linker: &mut Linker<StoreData>,
524 engine: &wasmtime::Engine,
525 abi: &StringAbi,
526 name: &'static str,
527 pick: fn(usize, f64) -> Option<usize>,
528) -> wasmtime::Result<()> {
529 let s = abi.value_type();
530 let abi = abi.clone();
531 register_host_fn(
532 linker,
533 MODULE_NAME,
534 method_key(name),
535 FuncType::new(engine, [s.clone(), ValType::F64], [s]),
536 move |caller, params, results| {
537 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
538 let arg = number(abi_arg(params, 1)?, name)?;
539 let unit = pick(recv.len, arg)
540 .map(|i| read_code_unit(&mut *caller, recv.array, i))
541 .transpose()?;
542 let out = Str::from_units(unit.map_or_else(Vec::new, |unit| vec![unit]));
543 *abi_result(results, 0)? = abi.write(caller, recv.vtable, &out)?;
544 Ok(())
545 },
546 )
547}
548
549fn reg_string_index(
553 linker: &mut Linker<StoreData>,
554 engine: &wasmtime::Engine,
555 abi: &StringAbi,
556) -> wasmtime::Result<()> {
557 let s = abi.value_type();
558 let abi = abi.clone();
559 register_host_fn(
560 linker,
561 MODULE_NAME,
562 crate::mangle::prelude("string_index"),
563 FuncType::new(engine, [s.clone(), ValType::F64], [s]),
564 move |caller, params, results| {
565 let recv = abi.payload(caller, abi_arg(params, 0)?, "string_index")?;
566 let arg = number(abi_arg(params, 1)?, "string_index")?;
567 let Some(index) = string_index_unit(recv.len, arg) else {
568 return Err(range_error(crate::codegen::bounds::INDEX_OOB_MESSAGE));
569 };
570 let unit = read_code_unit(&mut *caller, recv.array, index)?;
571 *abi_result(results, 0)? =
572 abi.write(caller, recv.vtable, &Str::from_units(vec![unit]))?;
573 Ok(())
574 },
575 )
576}
577
578fn reg_unit_to_str_or_undefined(
582 linker: &mut Linker<StoreData>,
583 engine: &wasmtime::Engine,
584 abi: &StringAbi,
585 name: &'static str,
586 pick: fn(usize, f64) -> Option<usize>,
587) -> wasmtime::Result<()> {
588 let s = abi.value_type();
589 let object = ValType::Ref(RefType::new(
590 true,
591 HeapType::ConcreteStruct(build_intrinsic_types(engine)?.object),
592 ));
593 let abi = abi.clone();
594 register_host_fn(
595 linker,
596 MODULE_NAME,
597 method_key(name),
598 FuncType::new(engine, [s, ValType::F64], [object]),
599 move |caller, params, results| {
600 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
601 let arg = number(abi_arg(params, 1)?, name)?;
602 *abi_result(results, 0)? = match pick(recv.len, arg) {
603 Some(i) => {
604 let unit = read_code_unit(&mut *caller, recv.array, i)?;
605 abi.write(caller, recv.vtable, &Str::from_units(vec![unit]))?
606 }
607 None => crate::runtime::prelude::undefined::value(caller)?,
608 };
609 Ok(())
610 },
611 )
612}
613
614fn reg_char_code_at(
616 linker: &mut Linker<StoreData>,
617 engine: &wasmtime::Engine,
618 abi: &StringAbi,
619) -> wasmtime::Result<()> {
620 const NAME: &str = "charCodeAt";
621 let s = abi.value_type();
622 let abi = abi.clone();
623 register_host_fn(
624 linker,
625 MODULE_NAME,
626 method_key(NAME),
627 FuncType::new(engine, [s, ValType::F64], [ValType::F64]),
628 move |caller, params, results| {
629 let recv = abi.payload(caller, abi_arg(params, 0)?, NAME)?;
630 let arg = number(abi_arg(params, 1)?, NAME)?;
631 let value = match unit_index(recv.len, arg) {
632 Some(i) => f64::from(read_code_unit(&mut *caller, recv.array, i)?),
633 None => f64::NAN,
634 };
635 *abi_result(results, 0)? = Val::F64(value.to_bits());
636 Ok(())
637 },
638 )
639}
640
641fn reg_code_point_at(
644 linker: &mut Linker<StoreData>,
645 engine: &wasmtime::Engine,
646 abi: &StringAbi,
647) -> wasmtime::Result<()> {
648 const NAME: &str = "codePointAt";
649 let s = abi.value_type();
650 let object = ValType::Ref(RefType::new(
651 true,
652 HeapType::ConcreteStruct(build_intrinsic_types(engine)?.object),
653 ));
654 let abi = abi.clone();
655 register_host_fn(
656 linker,
657 MODULE_NAME,
658 method_key(NAME),
659 FuncType::new(engine, [s, ValType::F64], [object]),
660 move |caller, params, results| {
661 let recv = abi.payload(caller, abi_arg(params, 0)?, NAME)?;
662 let arg = number(abi_arg(params, 1)?, NAME)?;
663 let Some(i) = unit_index(recv.len, arg) else {
664 *abi_result(results, 0)? = crate::runtime::prelude::undefined::value(caller)?;
665 return Ok(());
666 };
667 let unit = read_code_unit(&mut *caller, recv.array, i)?;
668 let next = if i + 1 < recv.len {
669 Some(read_code_unit(&mut *caller, recv.array, i + 1)?)
670 } else {
671 None
672 };
673 let boxed = write_boxed_number_struct(caller, code_point(unit, next))?;
674 *abi_result(results, 0)? = Val::AnyRef(Some(boxed.to_anyref()));
675 Ok(())
676 },
677 )
678}
679
680fn reg_repeat(
682 linker: &mut Linker<StoreData>,
683 engine: &wasmtime::Engine,
684 abi: &StringAbi,
685) -> wasmtime::Result<()> {
686 let s = abi.value_type();
687 let abi = abi.clone();
688 register_host_fn(
689 linker,
690 MODULE_NAME,
691 method_key("repeat"),
692 FuncType::new(engine, [s.clone(), ValType::F64], [s]),
693 move |caller, params, results| {
694 let recv = abi.payload(caller, abi_arg(params, 0)?, "repeat")?;
695 let count = number(abi_arg(params, 1)?, "repeat")?;
696 fuel::charge(&mut *caller, fuel::COPY, recv.len as u64)?;
697 let total = repeat_length(recv.len, count).map_err(throw)?;
698 fuel::charge(&mut *caller, fuel::COPY, total as u64)?;
699 let runs = [recv.cycle(total)];
700 *abi_result(results, 0)? = abi.build(caller, recv.vtable, &runs)?;
701 Ok(())
702 },
703 )
704}
705
706fn reg_range_to_str(
709 linker: &mut Linker<StoreData>,
710 engine: &wasmtime::Engine,
711 abi: &StringAbi,
712 name: &'static str,
713 range: fn(usize, f64, f64) -> std::ops::Range<usize>,
714) -> wasmtime::Result<()> {
715 let s = abi.value_type();
716 let abi = abi.clone();
717 register_host_fn(
718 linker,
719 MODULE_NAME,
720 method_key(name),
721 FuncType::new(engine, [s.clone(), ValType::F64, ValType::F64], [s]),
722 move |caller, params, results| {
723 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
724 let a = number(abi_arg(params, 1)?, name)?;
725 let b = number(abi_arg(params, 2)?, name)?;
726 let kept = range(recv.len, a, b);
727 let units = read_code_units_range(&mut *caller, recv.array, kept.start, kept.len())?;
728 *abi_result(results, 0)? = abi.write(caller, recv.vtable, &Str::from_units(units))?;
729 Ok(())
730 },
731 )
732}
733
734fn reg_str_str_num_to_num(
736 linker: &mut Linker<StoreData>,
737 engine: &wasmtime::Engine,
738 abi: &StringAbi,
739 name: &'static str,
740 op: fn(&Str, &Str, f64) -> f64,
741) -> wasmtime::Result<()> {
742 let s = abi.value_type();
743 let abi = abi.clone();
744 register_host_fn(
745 linker,
746 MODULE_NAME,
747 method_key(name),
748 FuncType::new(engine, [s.clone(), s, ValType::F64], [ValType::F64]),
749 move |caller, params, results| {
750 let recv = abi.read(caller, abi_arg(params, 0)?, name)?;
751 let search = abi.read(caller, abi_arg(params, 1)?, name)?;
752 let from = number(abi_arg(params, 2)?, name)?;
753 fuel::charge(
754 &mut *caller,
755 fuel::SCAN,
756 (recv.value.len() + search.value.len()) as u64,
757 )?;
758 *abi_result(results, 0)? = Val::F64(op(&recv.value, &search.value, from).to_bits());
759 Ok(())
760 },
761 )
762}
763
764fn register_string_search_predicate(
766 linker: &mut Linker<StoreData>,
767 engine: &wasmtime::Engine,
768 abi: &StringAbi,
769 name: &'static str,
770 op: fn(&Str, &Str, f64) -> bool,
771) -> wasmtime::Result<()> {
772 let intr = build_intrinsic_types(engine)?;
773 let value = ValType::Ref(RefType::new(true, HeapType::ConcreteStruct(intr.object)));
774 let abi = abi.clone();
775 register_host_fn_async(
776 linker,
777 MODULE_NAME,
778 method_key(name),
779 FuncType::new(
780 engine,
781 [abi.value_type(), value.clone(), value],
782 [ValType::I32],
783 ),
784 move |caller, params, results| {
785 let abi = abi.clone();
786 Box::pin(async move {
787 let recv = abi.read(caller, abi_arg(params, 0)?, name)?;
788 let search =
789 super::super::value::search_string(caller, abi_arg(params, 1)?).await?;
790 let from = super::super::value::to_number(caller, abi_arg(params, 2)?).await?;
791 fuel::charge(
792 &mut *caller,
793 fuel::SCAN,
794 (recv.value.len() + search.len()) as u64,
795 )?;
796 *abi_result(results, 0)? =
797 Val::I32(op(&recv.value, &Str::from_units(search), from) as i32);
798 Ok(())
799 })
800 },
801 )
802}
803
804fn reg_window_predicate(
808 linker: &mut Linker<StoreData>,
809 engine: &wasmtime::Engine,
810 abi: &StringAbi,
811 name: &'static str,
812 window: fn(usize, usize, f64) -> Option<std::ops::Range<usize>>,
813) -> wasmtime::Result<()> {
814 let intr = build_intrinsic_types(engine)?;
815 let value = ValType::Ref(RefType::new(true, HeapType::ConcreteStruct(intr.object)));
816 let abi = abi.clone();
817 register_host_fn_async(
818 linker,
819 MODULE_NAME,
820 method_key(name),
821 FuncType::new(
822 engine,
823 [abi.value_type(), value.clone(), value],
824 [ValType::I32],
825 ),
826 move |caller, params, results| {
827 let abi = abi.clone();
828 Box::pin(async move {
829 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
830 let needle =
831 super::super::value::search_string(caller, abi_arg(params, 1)?).await?;
832 let position = super::super::value::to_number(caller, abi_arg(params, 2)?).await?;
833 let found = match window(recv.len, needle.len(), position) {
834 Some(range) => {
835 read_code_units_range(caller, recv.array, range.start, range.len())?
836 == needle
837 }
838 None => false,
839 };
840 *abi_result(results, 0)? = Val::I32(found as i32);
841 Ok(())
842 })
843 },
844 )
845}
846
847fn reg_concat(
850 linker: &mut Linker<StoreData>,
851 engine: &wasmtime::Engine,
852 abi: &StringAbi,
853) -> wasmtime::Result<()> {
854 let s = abi.value_type();
855 let array = ValType::Ref(RefType::new(
856 false,
857 HeapType::ConcreteStruct(crate::runtime::host::intrinsic_array_type(engine)?),
858 ));
859 let abi = abi.clone();
860 register_host_fn(
861 linker,
862 MODULE_NAME,
863 method_key("concat"),
864 FuncType::new(engine, [s.clone(), array.clone()], [s]),
865 move |caller, params, results| {
866 *abi_result(results, 0)? =
867 abi.concat_all(caller, abi_arg(params, 0)?, abi_arg(params, 1)?)?;
868 Ok(())
869 },
870 )
871}
872
873#[derive(Clone, Copy)]
875enum PadSide {
876 Start,
877 End,
878}
879
880fn reg_pad(
882 linker: &mut Linker<StoreData>,
883 engine: &wasmtime::Engine,
884 abi: &StringAbi,
885 name: &'static str,
886 side: PadSide,
887) -> wasmtime::Result<()> {
888 let s = abi.value_type();
889 let abi = abi.clone();
890 register_host_fn(
891 linker,
892 MODULE_NAME,
893 method_key(name),
894 FuncType::new(engine, [s.clone(), ValType::F64, s.clone()], [s]),
895 move |caller, params, results| {
896 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
897 let target = number(abi_arg(params, 1)?, name)?;
898 let pad = abi.payload(caller, abi_arg(params, 2)?, name)?;
899 fuel::charge(&mut *caller, fuel::COPY, recv.len as u64 + pad.len as u64)?;
900 let total = pad_length(recv.len, target, pad.len).map_err(throw)?;
901 fuel::charge(&mut *caller, fuel::COPY, total as u64)?;
902 let fill_len = total
904 .checked_sub(recv.len)
905 .ok_or_else(|| fatal_host_error("padded string shorter than its receiver"))?;
906 let fill = pad.cycle(fill_len);
907 let runs = match side {
908 PadSide::Start => [fill, recv.whole()],
909 PadSide::End => [recv.whole(), fill],
910 };
911 *abi_result(results, 0)? = abi.build(caller, recv.vtable, &runs)?;
912 Ok(())
913 },
914 )
915}
916
917fn reg_str_str_to_str_fallible(
920 linker: &mut Linker<StoreData>,
921 engine: &wasmtime::Engine,
922 abi: &StringAbi,
923 name: &'static str,
924 op: fn(&Str, &Str) -> super::Result<Str>,
925) -> wasmtime::Result<()> {
926 let s = abi.value_type();
927 let abi = abi.clone();
928 register_host_fn(
929 linker,
930 MODULE_NAME,
931 method_key(name),
932 FuncType::new(engine, [s.clone(), s.clone()], [s]),
933 move |caller, params, results| {
934 let recv = abi.read(caller, abi_arg(params, 0)?, name)?;
935 let arg = abi.read(caller, abi_arg(params, 1)?, name)?;
936 let out = op(&recv.value, &arg.value).map_err(throw)?;
937 *abi_result(results, 0)? = abi.write(caller, recv.vtable, &out)?;
938 Ok(())
939 },
940 )
941}
942
943fn reg_str_to_bool(
945 linker: &mut Linker<StoreData>,
946 engine: &wasmtime::Engine,
947 abi: &StringAbi,
948 name: &'static str,
949 op: fn(&Str) -> bool,
950) -> wasmtime::Result<()> {
951 let s = abi.value_type();
952 let abi = abi.clone();
953 register_host_fn(
954 linker,
955 MODULE_NAME,
956 method_key(name),
957 FuncType::new(engine, [s], [ValType::I32]),
958 move |caller, params, results| {
959 let recv = abi.read(caller, abi_arg(params, 0)?, name)?;
960 *abi_result(results, 0)? = Val::I32(op(&recv.value) as i32);
961 Ok(())
962 },
963 )
964}
965
966fn reg_str_to_str(
968 linker: &mut Linker<StoreData>,
969 engine: &wasmtime::Engine,
970 abi: &StringAbi,
971 name: &'static str,
972 op: fn(&Str) -> Str,
973) -> wasmtime::Result<()> {
974 let s = abi.value_type();
975 let abi = abi.clone();
976 register_host_fn(
977 linker,
978 MODULE_NAME,
979 method_key(name),
980 FuncType::new(engine, [s.clone()], [s]),
981 move |caller, params, results| {
982 let recv = abi.read(caller, abi_arg(params, 0)?, name)?;
983 fuel::charge(
985 &mut *caller,
986 fuel::SCAN,
987 2 * recv.value.units().len() as u64,
988 )?;
989 *abi_result(results, 0)? = abi.write(caller, recv.vtable, &op(&recv.value))?;
990 Ok(())
991 },
992 )
993}
994
995fn reg_case_map(
998 linker: &mut Linker<StoreData>,
999 engine: &wasmtime::Engine,
1000 abi: &StringAbi,
1001 name: &'static str,
1002 map: fn(&mut GcText<'_, '_>) -> wasmtime::Result<()>,
1003) -> wasmtime::Result<()> {
1004 let s = abi.value_type();
1005 let abi = abi.clone();
1006 register_host_fn(
1007 linker,
1008 MODULE_NAME,
1009 method_key(name),
1010 FuncType::new(engine, [s.clone()], [s]),
1011 move |caller, params, results| {
1012 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
1013 let len = recv.len as u64;
1016 fuel::charge(&mut *caller, fuel::COPY, len)?;
1017 fuel::charge(&mut *caller, fuel::SCAN, len)?;
1018 fuel::charge(&mut *caller, fuel::COPY, len)?;
1019 let guess = allocate(caller, &abi.payload_ty, recv.len)?;
1020 let input = GcUnits::new(caller, recv.array, recv.len);
1021 let mut text = GcText::new(input, abi.payload_ty.clone(), guess, recv.len);
1022 map(&mut text)?;
1023 let payload = text.finish()?;
1024 *abi_result(results, 0)? = abi.wrap(caller, recv.vtable, payload)?;
1025 Ok(())
1026 },
1027 )
1028}
1029
1030fn reg_trim(
1033 linker: &mut Linker<StoreData>,
1034 engine: &wasmtime::Engine,
1035 abi: &StringAbi,
1036 name: &'static str,
1037 ends: TrimEnds,
1038) -> wasmtime::Result<()> {
1039 let s = abi.value_type();
1040 let abi = abi.clone();
1041 register_host_fn(
1042 linker,
1043 MODULE_NAME,
1044 method_key(name),
1045 FuncType::new(engine, [s.clone()], [s]),
1046 move |caller, params, results| {
1047 let recv = abi.payload(caller, abi_arg(params, 0)?, name)?;
1048 fuel::charge(&mut *caller, fuel::COPY, recv.len as u64)?;
1050 fuel::charge(&mut *caller, fuel::SCAN, recv.len as u64)?;
1051 let kept = trim_range(&mut GcUnits::new(caller, recv.array, recv.len), ends)?;
1052 fuel::charge(&mut *caller, fuel::COPY, kept.len() as u64)?;
1053 *abi_result(results, 0)? = abi.build(caller, recv.vtable, &[recv.range_run(kept)])?;
1054 Ok(())
1055 },
1056 )
1057}
1058
1059#[derive(Clone)]
1062struct StringAbi {
1063 string_ty: StructType,
1064 payload_ty: ArrayType,
1065}
1066
1067struct Receiver {
1071 vtable: Val,
1072 value: Str,
1073}
1074
1075struct Payload {
1078 vtable: Val,
1079 array: Rooted<ArrayRef>,
1080 len: usize,
1081}
1082
1083impl Payload {
1084 fn range_run(&self, range: std::ops::Range<usize>) -> Run {
1086 Run::copy(self.array, range)
1087 }
1088
1089 fn whole(&self) -> Run {
1091 Run::copy(self.array, 0..self.len)
1092 }
1093
1094 fn cycle(&self, len: usize) -> Run {
1096 Run {
1097 source: self.array,
1098 start: 0,
1099 period: self.len,
1100 len,
1101 }
1102 }
1103
1104 fn read_all(&self, caller: &mut Caller<'_, StoreData>, name: &str) -> wasmtime::Result<Str> {
1105 Ok(Str::from_units(read_code_units(
1106 &mut *caller,
1107 self.array,
1108 name,
1109 )?))
1110 }
1111}
1112
1113impl StringAbi {
1114 fn recover(engine: &wasmtime::Engine) -> wasmtime::Result<Self> {
1115 Ok(Self {
1116 string_ty: intrinsic_string_type(engine)?,
1117 payload_ty: string_array_type(engine),
1118 })
1119 }
1120
1121 fn value_type(&self) -> ValType {
1122 ValType::Ref(RefType::new(
1123 false,
1124 HeapType::ConcreteStruct(self.string_ty.clone()),
1125 ))
1126 }
1127
1128 fn read(
1129 &self,
1130 caller: &mut Caller<'_, StoreData>,
1131 val: &Val,
1132 name: &str,
1133 ) -> wasmtime::Result<Receiver> {
1134 let payload = self.payload(caller, val, name)?;
1135 Ok(Receiver {
1136 vtable: payload.vtable,
1137 value: payload.read_all(caller, name)?,
1138 })
1139 }
1140
1141 fn payload(
1142 &self,
1143 caller: &mut Caller<'_, StoreData>,
1144 val: &Val,
1145 name: &str,
1146 ) -> wasmtime::Result<Payload> {
1147 let Val::AnyRef(Some(any)) = val else {
1148 return Err(wasmtime::Error::msg(format!(
1149 "{name} expects a string, got {val:?}"
1150 )));
1151 };
1152 let st = any
1153 .as_struct(&mut *caller)?
1154 .ok_or_else(|| wasmtime::Error::msg(format!("{name}: expected a $string struct")))?;
1155 let vtable = st.field(&mut *caller, 0)?;
1156 let array = match st.field(&mut *caller, 1)? {
1157 Val::AnyRef(Some(arr)) => arr.unwrap_array(&mut *caller)?,
1158 other => {
1159 return Err(wasmtime::Error::msg(format!(
1160 "{name}: malformed $string payload {other:?}"
1161 )));
1162 }
1163 };
1164 let len = usize::try_from(array.len(&mut *caller)?)
1165 .map_err(crate::runtime::host::fatal_host_error)?;
1166 Ok(Payload { vtable, array, len })
1167 }
1168
1169 fn write(
1170 &self,
1171 caller: &mut Caller<'_, StoreData>,
1172 vtable: Val,
1173 s: &Str,
1174 ) -> wasmtime::Result<Val> {
1175 fuel::charge(&mut *caller, fuel::COPY, s.units().len() as u64)?;
1176 self.write_units(caller, vtable, s)
1177 }
1178
1179 fn write_units(
1180 &self,
1181 caller: &mut Caller<'_, StoreData>,
1182 vtable: Val,
1183 s: &Str,
1184 ) -> wasmtime::Result<Val> {
1185 let pre = ArrayRefPre::new(&mut *caller, self.payload_ty.clone());
1186 let payload = ArrayRef::new_from_i16_slice(&mut *caller, &pre, s.units())?;
1187 self.wrap(caller, vtable, payload)
1188 }
1189
1190 fn concat(
1192 &self,
1193 caller: &mut Caller<'_, StoreData>,
1194 a: &Val,
1195 b: &Val,
1196 name: &str,
1197 ) -> wasmtime::Result<Val> {
1198 let a = self.payload(caller, a, name)?;
1199 let b = self.payload(caller, b, name)?;
1200 fuel::charge(&mut *caller, fuel::COPY, a.len as u64 + b.len as u64)?;
1201 let total = concat_length(a.len, b.len)?;
1202 fuel::charge(&mut *caller, fuel::COPY, total as u64)?;
1203 self.build(caller, a.vtable, &[a.whole(), b.whole()])
1204 }
1205
1206 fn concat_all(
1209 &self,
1210 caller: &mut Caller<'_, StoreData>,
1211 receiver: &Val,
1212 others: &Val,
1213 ) -> wasmtime::Result<Val> {
1214 let receiver = self.payload(caller, receiver, "concat")?;
1215 let others =
1216 crate::runtime::array_storage::ArrayStorage::read(caller, others)?.snapshot(caller)?;
1217 let mut runs = Vec::with_capacity(others.len() + 1);
1218 let mut total = receiver.len;
1219 runs.push(receiver.whole());
1220 for other in &others {
1221 let other = self.payload(caller, other, "concat")?;
1222 total = concat_length(total, other.len)?;
1223 runs.push(other.whole());
1224 }
1225 fuel::charge(&mut *caller, fuel::COPY, total as u64)?;
1226 self.build(caller, receiver.vtable, &runs)
1227 }
1228
1229 fn build(
1231 &self,
1232 caller: &mut Caller<'_, StoreData>,
1233 vtable: Val,
1234 runs: &[Run],
1235 ) -> wasmtime::Result<Val> {
1236 let total = runs
1237 .iter()
1238 .try_fold(0usize, |total, run| total.checked_add(run.len))
1239 .ok_or_else(|| fatal_host_error("string length overflow"))?;
1240 let payload = allocate(caller, &self.payload_ty, total)?;
1241 let mut at = 0;
1242 for run in runs {
1243 fill_run(&mut *caller, payload, at, run)?;
1244 at += run.len;
1245 }
1246 self.wrap(caller, vtable, payload)
1247 }
1248
1249 fn wrap(
1250 &self,
1251 caller: &mut Caller<'_, StoreData>,
1252 vtable: Val,
1253 payload: Rooted<ArrayRef>,
1254 ) -> wasmtime::Result<Val> {
1255 let pre = StructRefPre::new(&mut *caller, self.string_ty.clone());
1256 let st = StructRef::new(
1257 &mut *caller,
1258 &pre,
1259 &[vtable, Val::AnyRef(Some(payload.to_anyref())), Val::I64(0)],
1260 )?;
1261 Ok(Val::AnyRef(Some(st.to_anyref())))
1262 }
1263}
1264
1265pub(crate) async fn string_ctor_call(
1271 caller: &mut Caller<'_, StoreData>,
1272 value: &Val,
1273) -> wasmtime::Result<Val> {
1274 let Val::AnyRef(Some(any)) = value else {
1275 let st = write_submilli_string_struct(caller, "null")?;
1276 return Ok(Val::AnyRef(Some(st.to_anyref())));
1277 };
1278 let intr = intrinsic_types(&mut *caller)?;
1279 if let Some(st) = any.as_struct(&mut *caller)?
1280 && StructType::eq(&st.ty(&*caller)?, &intr.bigint)
1281 {
1282 let (sign, limbs) = crate::runtime::prelude::bigint::ops::read_bigint_struct(
1283 caller,
1284 value,
1285 "String(bigint)",
1286 )?;
1287 let value = crate::runtime::prelude::bigint::ops::limbs_to_bigint(sign, &limbs)?;
1288 let text = crate::runtime::prelude::bigint::ops::format_bigint(caller, &value, 10)?;
1289 let st = write_submilli_string_struct(caller, &text)?;
1290 return Ok(Val::AnyRef(Some(st.to_anyref())));
1291 }
1292 crate::runtime::prelude::vtable::dispatch_vtable_slot(caller, value, 0, &[]).await
1293}
1294
1295fn read_number_array(
1296 caller: &mut Caller<'_, StoreData>,
1297 val: &Val,
1298 name: &str,
1299) -> wasmtime::Result<Vec<f64>> {
1300 let storage = crate::runtime::array_storage::ArrayStorage::read(caller, val)?;
1301 let backing = storage.backing;
1302 let len = storage.len;
1303 let mut nums = Vec::new();
1304 nums.try_reserve_exact(len as usize)
1305 .map_err(crate::runtime::host::fatal_host_error)?;
1306 for i in 0..len {
1307 let boxed = match backing.get(&mut *caller, i)? {
1308 Val::AnyRef(Some(b)) => b
1309 .as_struct(&mut *caller)?
1310 .ok_or_else(|| wasmtime::Error::msg(format!("{name}: arg {i} not a struct")))?,
1311 other => {
1312 return Err(wasmtime::Error::msg(format!(
1313 "{name}: arg {i} is {other:?}, not a boxed number"
1314 )));
1315 }
1316 };
1317 match boxed.field(&mut *caller, 1)? {
1318 Val::F64(bits) => nums.push(f64::from_bits(bits)),
1319 other => {
1320 return Err(wasmtime::Error::msg(format!(
1321 "{name}: arg {i} payload {other:?} is not f64"
1322 )));
1323 }
1324 }
1325 }
1326 Ok(nums)
1327}
1328
1329fn number(val: &Val, name: &str) -> wasmtime::Result<f64> {
1330 match val {
1331 Val::F64(bits) => Ok(f64::from_bits(*bits)),
1332 other => Err(wasmtime::Error::msg(format!(
1333 "{name} expects f64, got {other:?}"
1334 ))),
1335 }
1336}
1337
1338fn throw(e: RangeError) -> wasmtime::Error {
1339 range_error(e.message())
1340}
1341
1342#[allow(clippy::too_many_lines)]
1345pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
1346 use crate::runtime::prelude::declaration::doc;
1347 use crate::{
1348 Dispatch, MethodSig, Param, PropertySig, Span, Type, TypeKind, TypeSymbol, ValueKind,
1349 ValueSymbol,
1350 };
1351 use std::collections::BTreeMap;
1352 defs.values.insert(
1353 "string_concat".to_string(),
1354 ValueSymbol {
1355 name: "string_concat".to_string(),
1356 mangled_name: crate::mangle::prelude("string_concat"),
1357 declaration_span: Span::at(crate::FileId::PRELUDE),
1358 kind: ValueKind::Function {
1359 generics: Vec::new(),
1360 params: vec![Param::anon(Type::String), Param::anon(Type::String)],
1361 ret: Type::String,
1362 type_predicate: None,
1363 doc: doc("/** Internal: concatenate two strings. */"),
1364 },
1365 },
1366 );
1367 defs.values.insert(
1368 "string_eq".to_string(),
1369 ValueSymbol {
1370 name: "string_eq".to_string(),
1371 mangled_name: crate::mangle::prelude("string_eq"),
1372 declaration_span: Span::at(crate::FileId::PRELUDE),
1373 kind: ValueKind::Function {
1374 generics: Vec::new(),
1375 params: vec![Param::anon(Type::String), Param::anon(Type::String)],
1376 ret: Type::Boolean,
1377 type_predicate: None,
1378 doc: doc("/** Internal: structural string equality. */"),
1379 },
1380 },
1381 );
1382 defs.values.insert(
1383 "string_length".to_string(),
1384 ValueSymbol {
1385 name: "string_length".to_string(),
1386 mangled_name: crate::mangle::prelude("string_length"),
1387 declaration_span: Span::at(crate::FileId::PRELUDE),
1388 kind: ValueKind::Function {
1389 generics: Vec::new(),
1390 params: vec![Param::anon(Type::String)],
1391 ret: Type::Number,
1392 type_predicate: None,
1393 doc: doc("/** Internal: UTF-16 code-unit length. */"),
1394 },
1395 },
1396 );
1397 defs.values.insert(
1398 "string_index".to_string(),
1399 ValueSymbol {
1400 name: "string_index".to_string(),
1401 mangled_name: crate::mangle::prelude("string_index"),
1402 declaration_span: Span::at(crate::FileId::PRELUDE),
1403 kind: ValueKind::Function {
1404 generics: Vec::new(),
1405 params: vec![Param::anon(Type::String), Param::anon(Type::Number)],
1406 ret: Type::String,
1407 type_predicate: None,
1408 doc: doc("/** Internal: `s[index]`, throwing `RangeError` out of range. */"),
1409 },
1410 },
1411 );
1412 defs.values.insert(
1415 "string_cmp".to_string(),
1416 ValueSymbol {
1417 name: "string_cmp".to_string(),
1418 mangled_name: crate::mangle::prelude("string_cmp"),
1419 declaration_span: Span::at(crate::FileId::PRELUDE),
1420 kind: ValueKind::Function {
1421 generics: Vec::new(),
1422 params: vec![Param::anon(Type::String), Param::anon(Type::String)],
1423 ret: Type::Boolean,
1424 type_predicate: None,
1425 doc: doc("/** Internal: lexicographic compare; i32 sign result. */"),
1426 },
1427 },
1428 );
1429
1430 for name in [
1431 "string_true",
1432 "string_false",
1433 "string_comma",
1434 "string_object_object",
1435 "string_object_function",
1436 "string_null",
1437 ] {
1438 defs.values.insert(
1439 name.to_string(),
1440 ValueSymbol {
1441 name: name.to_string(),
1442 mangled_name: crate::mangle::prelude(name),
1443 declaration_span: Span::at(crate::FileId::PRELUDE),
1444 kind: ValueKind::Const {
1445 ty: Type::String,
1446 doc: None,
1447 },
1448 },
1449 );
1450 }
1451 defs.types.insert(
1452 "String".to_string(),
1453 TypeSymbol {
1454 name: "String".to_string(),
1455 mangled_name: crate::mangle::prelude("String"),
1456 declaration_span: Span::at(crate::FileId::PRELUDE),
1457 kind: TypeKind::Interface { index: None,
1458 generics: Vec::new(),
1459 methods: BTreeMap::from([
1460 (
1461 "toString".to_string(),
1462 MethodSig {
1463 optional: false,
1464 generics: Vec::new(),
1465 params: Vec::new(),
1466 ret: Type::String,
1467 predicate: None,
1468 doc: doc("/** Returns this string unchanged (identity). */"),
1469 },
1470 ),
1471 (
1472 "toJson".to_string(),
1473 MethodSig {
1474 optional: false,
1475 generics: Vec::new(),
1476 params: Vec::new(),
1477 ret: Type::String,
1478 predicate: None,
1479 doc: doc(
1480 "/** Returns this string wrapped in `\"…\"` with JSON escapes applied (`\\\"`, `\\\\`, `\\b`, `\\f`, `\\n`, `\\r`, `\\t`, and `\\u00XX` for control codepoints). */",
1481 ),
1482 },
1483 ),
1484 (
1485 "concat".to_string(),
1486 MethodSig {
1487 optional: false,
1488 generics: Vec::new(),
1489 params: vec![Param::rest(
1490 "strings",
1491 Type::Array(Box::new(Type::String)),
1492 )],
1493 ret: Type::String,
1494 predicate: None,
1495 doc: doc(
1496 "/**\n * Returns a new string with each of `strings` appended, in order.\n * The receiver is not modified.\n * @param strings Zero or more strings to append.\n */",
1497 ),
1498 },
1499 ),
1500 (
1501 "equals".to_string(),
1502 MethodSig {
1503 optional: false,
1504 generics: Vec::new(),
1505 params: vec![Param::new("other", Type::String)],
1506 ret: Type::Boolean,
1507 predicate: None,
1508 doc: doc(
1509 "/**\n * Returns `true` when both strings have identical code units.\n * @param other String to compare against.\n */",
1510 ),
1511 },
1512 ),
1513 (
1514 "localeCompare".to_string(),
1515 MethodSig {
1516 optional: false,
1517 generics: Vec::new(),
1518 params: vec![Param::new("other", Type::String)],
1519 ret: Type::Number,
1520 predicate: None,
1521 doc: doc(
1522 "/**\n * Compares this string with `other` in UTF-16 code-unit order.\n * @param other The string to compare against.\n * @returns A negative number if this sorts before `other`, `0` if equal, a positive number if after. No locale awareness — pure code-unit order.\n */",
1523 ),
1524 },
1525 ),
1526 (
1527 "iterator".to_string(),
1528 MethodSig {
1529 optional: false,
1530 generics: Vec::new(),
1531 params: Vec::new(),
1532 ret: Type::prelude_interface("Iterator".to_string(), vec![Type::String]),
1533 predicate: None,
1534 doc: doc(
1535 "/** Returns an `Iterator<string>` of CODE POINTS — surrogate pairs arrive as one two-unit string. Drives `for-of` over a string. */",
1536 ),
1537 },
1538 ),
1539 (
1540 "isWellFormed".to_string(),
1541 MethodSig {
1542 optional: false,
1543 generics: Vec::new(),
1544 params: Vec::new(),
1545 ret: Type::Boolean,
1546 predicate: None,
1547 doc: doc(
1548 "/** Returns `true` when the string contains no lone surrogates (it round-trips losslessly through UTF-8). */",
1549 ),
1550 },
1551 ),
1552 (
1553 "toWellFormed".to_string(),
1554 MethodSig {
1555 optional: false,
1556 generics: Vec::new(),
1557 params: Vec::new(),
1558 ret: Type::String,
1559 predicate: None,
1560 doc: doc(
1561 "/** Returns a copy with every lone surrogate replaced by U+FFFD (`\u{fffd}`); well-formed strings come back unchanged. */",
1562 ),
1563 },
1564 ),
1565 (
1566 "charAt".to_string(),
1567 MethodSig {
1568 optional: false,
1569 generics: Vec::new(),
1570 params: vec![Param::new("index", Type::Number)],
1571 ret: Type::String,
1572 predicate: None,
1573 doc: doc(
1574 "/**\n * Returns the UTF-16 code unit at `index` as a single-character string.\n * Returns `\"\"` if `index` is negative or beyond the string length.\n * @param index Zero-based index of the code unit.\n */",
1575 ),
1576 },
1577 ),
1578 (
1579 "at".to_string(),
1580 MethodSig {
1581 optional: false,
1582 generics: Vec::new(),
1583 params: vec![Param::new("index", Type::Number)],
1584 ret: Type::Union(vec![Type::String, Type::Undefined]),
1585 predicate: None,
1586 doc: doc(
1587 "/**\n * Returns the code unit at `index` as a single-character string. Negative `index` counts from the end.\n * Returns `undefined` on out-of-range. Use `charAt` for the `\"\"`-on-miss form.\n * @param index Zero-based index; negatives count from the end.\n */",
1588 ),
1589 },
1590 ),
1591 (
1592 "charCodeAt".to_string(),
1593 MethodSig {
1594 optional: false,
1595 generics: Vec::new(),
1596 params: vec![Param::new("index", Type::Number)],
1597 ret: Type::Number,
1598 predicate: None,
1599 doc: doc(
1600 "/**\n * Returns the UTF-16 code unit at `index` as an integer (0..65535), or `NaN` if out of range.\n * @param index Zero-based index of the code unit.\n */",
1601 ),
1602 },
1603 ),
1604 (
1605 "codePointAt".to_string(),
1606 MethodSig {
1607 optional: false,
1608 generics: Vec::new(),
1609 params: vec![Param::new("index", Type::Number)],
1610 ret: Type::Union(vec![Type::Number, Type::Undefined]),
1611 predicate: None,
1612 doc: doc(
1613 "/**\n * Returns the Unicode code point starting at `index`, decoding surrogate pairs into values up to 0x10FFFF.\n * Returns `undefined` if `index` is out of range.\n * @param index Zero-based index of the first code unit of the code point.\n */",
1614 ),
1615 },
1616 ),
1617 (
1618 "slice".to_string(),
1619 MethodSig {
1620 optional: false,
1621 generics: Vec::new(),
1622 params: vec![
1623 Param::with_default(
1624 "start",
1625 Type::Number,
1626 crate::DefaultValue::Number(0.0),
1627 ),
1628 Param::with_default(
1630 "end",
1631 Type::Number,
1632 crate::DefaultValue::Number(f64::INFINITY),
1633 ),
1634 ],
1635 ret: Type::String,
1636 predicate: None,
1637 doc: doc(
1638 "/**\n * Returns a new string containing the code units from `start` (inclusive) to `end` (exclusive).\n * Negative values count from the end. The receiver is not modified.\n * @param start First index to copy. Defaults to `0`.\n * @param end Index one past the last to copy. Defaults to the string length.\n */",
1639 ),
1640 },
1641 ),
1642 (
1643 "substr".to_string(),
1644 MethodSig {
1645 optional: false,
1646 generics: Vec::new(),
1647 params: vec![
1648 Param::new("start", Type::Number),
1649 Param::with_default(
1650 "length",
1651 Type::Number,
1652 crate::DefaultValue::Number(f64::INFINITY),
1653 ),
1654 ],
1655 ret: Type::String,
1656 predicate: None,
1657 doc: doc(
1658 "/**\n * Returns `length` code units starting at `start`; a negative `start` counts from the end. Deprecated in JavaScript in favour of `slice`.\n * @param start First index to copy.\n * @param length How many code units to copy. Defaults to the rest of the string.\n */",
1659 ),
1660 },
1661 ),
1662 (
1663 "substring".to_string(),
1664 MethodSig {
1665 optional: false,
1666 generics: Vec::new(),
1667 params: vec![
1668 Param::with_default(
1669 "start",
1670 Type::Number,
1671 crate::DefaultValue::Number(0.0),
1672 ),
1673 Param::with_default(
1674 "end",
1675 Type::Number,
1676 crate::DefaultValue::Number(f64::INFINITY),
1677 ),
1678 ],
1679 ret: Type::String,
1680 predicate: None,
1681 doc: doc(
1682 "/**\n * Like `slice`, but negative values clamp to `0`, and `start` and `end` are swapped when `start > end`.\n * @param start First index to copy. Defaults to `0`.\n * @param end Index one past the last to copy. Defaults to the string length.\n */",
1683 ),
1684 },
1685 ),
1686 (
1687 "indexOf".to_string(),
1688 MethodSig {
1689 optional: false,
1690 generics: Vec::new(),
1691 params: vec![
1692 Param::new("search", Type::String),
1693 Param::with_default(
1694 "fromIndex",
1695 Type::Number,
1696 crate::DefaultValue::Number(0.0),
1697 ),
1698 ],
1699 ret: Type::Number,
1700 predicate: None,
1701 doc: doc(
1702 "/**\n * Returns the index of the first occurrence of `search` at or after `fromIndex`, or `-1` if none.\n * An empty `search` returns `min(fromIndex, length)`.\n * @param search Substring to search for.\n * @param fromIndex Index to begin searching from.\n */",
1703 ),
1704 },
1705 ),
1706 (
1707 "lastIndexOf".to_string(),
1708 MethodSig {
1709 optional: false,
1710 generics: Vec::new(),
1711 params: vec![
1712 Param::new("search", Type::String),
1713 Param::with_default(
1714 "fromIndex",
1715 Type::Number,
1716 crate::DefaultValue::Number(f64::INFINITY),
1717 ),
1718 ],
1719 ret: Type::Number,
1720 predicate: None,
1721 doc: doc(
1722 "/**\n * Returns the index of the last occurrence of `search` at or before `fromIndex`, or `-1` if none.\n * @param search Substring to search for.\n * @param fromIndex Highest valid start position to consider. Defaults to the string length.\n */",
1723 ),
1724 },
1725 ),
1726 (
1727 "includes".to_string(),
1728 MethodSig {
1729 optional: false,
1730 generics: Vec::new(),
1731 params: vec![
1732 Param::new("search", Type::String),
1733 Param::with_default(
1734 "fromIndex",
1735 Type::Number,
1736 crate::DefaultValue::Number(0.0),
1737 ),
1738 ],
1739 ret: Type::Boolean,
1740 predicate: None,
1741 doc: doc(
1742 "/**\n * Returns `true` if `search` occurs at or after `fromIndex`.\n * @param search Substring to search for.\n * @param fromIndex Index to begin searching from.\n */",
1743 ),
1744 },
1745 ),
1746 (
1747 "startsWith".to_string(),
1748 MethodSig {
1749 optional: false,
1750 generics: Vec::new(),
1751 params: vec![
1752 Param::new("search", Type::String),
1753 Param::with_default(
1754 "position",
1755 Type::Number,
1756 crate::DefaultValue::Number(0.0),
1757 ),
1758 ],
1759 ret: Type::Boolean,
1760 predicate: None,
1761 doc: doc(
1762 "/**\n * Returns `true` if the substring starting at `position` begins with `search`.\n * @param search Prefix to test for.\n * @param position Index at which to start the comparison.\n */",
1763 ),
1764 },
1765 ),
1766 (
1767 "endsWith".to_string(),
1768 MethodSig {
1769 optional: false,
1770 generics: Vec::new(),
1771 params: vec![
1772 Param::new("search", Type::String),
1773 Param::with_default(
1774 "endPosition",
1775 Type::Number,
1776 crate::DefaultValue::Number(f64::INFINITY),
1777 ),
1778 ],
1779 ret: Type::Boolean,
1780 predicate: None,
1781 doc: doc(
1782 "/**\n * Returns `true` if the substring ending at `endPosition` ends with `search`.\n * @param search Suffix to test for.\n * @param endPosition Index marking the end (exclusive) of the substring considered.\n */",
1783 ),
1784 },
1785 ),
1786 (
1787 "repeat".to_string(),
1788 MethodSig {
1789 optional: false,
1790 generics: Vec::new(),
1791 params: vec![Param::new("count", Type::Number)],
1792 ret: Type::String,
1793 predicate: None,
1794 doc: doc(
1795 "/**\n * Returns a new string containing `count` copies of this string concatenated.\n * Zero `count` (or an empty receiver) returns `\"\"`; a negative `count` throws a `RangeError`.\n * @param count Number of repetitions.\n */",
1796 ),
1797 },
1798 ),
1799 (
1800 "padStart".to_string(),
1801 MethodSig {
1802 optional: false,
1803 generics: Vec::new(),
1804 params: vec![
1805 Param::new("targetLength", Type::Number),
1806 Param::with_default(
1807 "padString",
1808 Type::String,
1809 crate::DefaultValue::String(" ".to_string()),
1810 ),
1811 ],
1812 ret: Type::String,
1813 predicate: None,
1814 doc: doc(
1815 "/**\n * Pads this string with `padString` on the left until the result reaches `targetLength` code units. Returns this string unchanged when no padding is needed or when `padString` is empty.\n * @param targetLength Length of the padded result.\n * @param padString Filler repeated to reach the target.\n */",
1816 ),
1817 },
1818 ),
1819 (
1820 "padEnd".to_string(),
1821 MethodSig {
1822 optional: false,
1823 generics: Vec::new(),
1824 params: vec![
1825 Param::new("targetLength", Type::Number),
1826 Param::with_default(
1827 "padString",
1828 Type::String,
1829 crate::DefaultValue::String(" ".to_string()),
1830 ),
1831 ],
1832 ret: Type::String,
1833 predicate: None,
1834 doc: doc(
1835 "/**\n * Pads this string with `padString` on the right until the result reaches `targetLength` code units. Returns this string unchanged when no padding is needed or when `padString` is empty.\n * @param targetLength Length of the padded result.\n * @param padString Filler repeated to reach the target.\n */",
1836 ),
1837 },
1838 ),
1839 (
1840 "toUpperCase".to_string(),
1841 MethodSig {
1842 optional: false,
1843 generics: Vec::new(),
1844 params: Vec::new(),
1845 ret: Type::String,
1846 predicate: None,
1847 doc: doc(
1848 "/**\n * Returns a new string with every character converted to its Unicode uppercase form.\n */",
1849 ),
1850 },
1851 ),
1852 (
1853 "toLowerCase".to_string(),
1854 MethodSig {
1855 optional: false,
1856 generics: Vec::new(),
1857 params: Vec::new(),
1858 ret: Type::String,
1859 predicate: None,
1860 doc: doc(
1861 "/**\n * Returns a new string with every character converted to its Unicode lowercase form.\n */",
1862 ),
1863 },
1864 ),
1865 (
1866 "trim".to_string(),
1867 MethodSig {
1868 optional: false,
1869 generics: Vec::new(),
1870 params: Vec::new(),
1871 ret: Type::String,
1872 predicate: None,
1873 doc: doc(
1874 "/**\n * Returns a new string with Unicode whitespace removed from both ends.\n */",
1875 ),
1876 },
1877 ),
1878 (
1879 "trimStart".to_string(),
1880 MethodSig {
1881 optional: false,
1882 generics: Vec::new(),
1883 params: Vec::new(),
1884 ret: Type::String,
1885 predicate: None,
1886 doc: doc(
1887 "/**\n * Returns a new string with Unicode whitespace removed from the start.\n */",
1888 ),
1889 },
1890 ),
1891 (
1892 "trimLeft".to_string(),
1893 MethodSig {
1894 optional: false,
1895 generics: Vec::new(),
1896 params: Vec::new(),
1897 ret: Type::String,
1898 predicate: None,
1899 doc: doc(
1900 "/**\n * The older name of `trimStart`: returns a new string with Unicode whitespace removed from the start.\n */",
1901 ),
1902 },
1903 ),
1904 (
1905 "trimRight".to_string(),
1906 MethodSig {
1907 optional: false,
1908 generics: Vec::new(),
1909 params: Vec::new(),
1910 ret: Type::String,
1911 predicate: None,
1912 doc: doc(
1913 "/**\n * The older name of `trimEnd`: returns a new string with Unicode whitespace removed from the end.\n */",
1914 ),
1915 },
1916 ),
1917 (
1918 "trimEnd".to_string(),
1919 MethodSig {
1920 optional: false,
1921 generics: Vec::new(),
1922 params: Vec::new(),
1923 ret: Type::String,
1924 predicate: None,
1925 doc: doc(
1926 "/**\n * Returns a new string with Unicode whitespace removed from the end.\n */",
1927 ),
1928 },
1929 ),
1930 (
1931 "normalize".to_string(),
1932 MethodSig {
1933 optional: false,
1934 generics: Vec::new(),
1935 params: vec![Param::with_default(
1936 "form",
1937 Type::String,
1938 crate::DefaultValue::String("NFC".to_string()),
1939 )],
1940 ret: Type::String,
1941 predicate: None,
1942 doc: doc(
1943 "/**\n * Returns a new string in the specified Unicode normalization form.\n * @param form One of `\"NFC\"`, `\"NFD\"`, `\"NFKC\"`, `\"NFKD\"`; any other value throws `Error`. Defaults to `\"NFC\"`.\n */",
1944 ),
1945 },
1946 ),
1947 (
1952 "match".to_string(),
1953 MethodSig {
1954 optional: false,
1955 generics: Vec::new(),
1956 params: vec![Param::new(
1957 "re",
1958 Type::Union(vec![
1959 Type::String,
1960 Type::prelude_interface("RegExp".to_string(), Vec::new()),
1961 ]),
1962 )],
1963 ret: Type::Union(vec![
1964 Type::prelude_interface("RegExpMatch".to_string(), Vec::new()),
1965 Type::Null,
1966 ]),
1967 predicate: None,
1968 doc: doc(
1969 "/**\n * Find the next match of `re` in this string. Equivalent to `re.exec(this)` (v1; the `g`-flag array shape is a follow-up).\n * @param re The pattern to search for; a string is compiled as a pattern, as `new RegExp(re)` does.\n */",
1970 ),
1971 },
1972 ),
1973 (
1974 "search".to_string(),
1975 MethodSig {
1976 optional: false,
1977 generics: Vec::new(),
1978 params: vec![Param::new(
1979 "re",
1980 Type::Union(vec![
1981 Type::String,
1982 Type::prelude_interface("RegExp".to_string(), Vec::new()),
1983 ]),
1984 )],
1985 ret: Type::Number,
1986 predicate: None,
1987 doc: doc(
1988 "/**\n * Index of the first match of `re` in this string, or `-1` if no match.\n * @param re The pattern to search for; a string is compiled as a pattern, as `new RegExp(re)` does.\n */",
1989 ),
1990 },
1991 ),
1992 (
1993 "replace".to_string(),
1994 MethodSig {
1995 optional: false,
1996 generics: Vec::new(),
1997 params: vec![
1998 Param::new(
1999 "search",
2000 Type::Union(vec![
2001 Type::String,
2002 Type::prelude_interface("RegExp".to_string(), Vec::new()),
2003 ]),
2004 ),
2005 Param::new(
2006 "replacement",
2007 crate::runtime::prelude::regex::replacement_type(),
2008 ),
2009 ],
2010 ret: Type::String,
2011 predicate: None,
2012 doc: doc(
2013 "/**\n * Replace the first match of `search` with `replacement`. For a `RegExp` `search`, the replacement may reference numbered captures (`$1`, `$2`) and named captures (`$<name>`); for both `string` and `RegExp` arms, the special tokens `$$` (literal `$`), `$&` (matched substring), `` $` `` (substring before the match), and `$'` (substring after the match) are honoured.\n * @param search Substring or pattern to match.\n * @param replacement The replacement string, or a function called for each match with the matched text, each capture (`undefined` if it didn't participate), the match's offset, the whole string and, when the pattern names groups, an object of them, whose result replaces the match.\n */",
2014 ),
2015 },
2016 ),
2017 (
2018 "replaceAll".to_string(),
2019 MethodSig {
2020 optional: false,
2021 generics: Vec::new(),
2022 params: vec![
2023 Param::new(
2024 "search",
2025 Type::Union(vec![
2026 Type::String,
2027 Type::prelude_interface("RegExp".to_string(), Vec::new()),
2028 ]),
2029 ),
2030 Param::new(
2031 "replacement",
2032 crate::runtime::prelude::regex::replacement_type(),
2033 ),
2034 ],
2035 ret: Type::String,
2036 predicate: None,
2037 doc: doc(
2038 "/**\n * Replace every non-overlapping match of `search` with `replacement`. Replacement-token semantics match `replace`.\n * @param search Substring or pattern to match.\n * @param replacement The replacement string.\n */",
2039 ),
2040 },
2041 ),
2042 (
2043 "split".to_string(),
2044 MethodSig {
2045 optional: false,
2046 generics: Vec::new(),
2047 params: vec![
2048 Param::new(
2049 "separator",
2050 Type::Union(vec![
2051 Type::String,
2052 Type::prelude_interface("RegExp".to_string(), Vec::new()),
2053 ]),
2054 ),
2055 Param::with_default(
2056 "limit",
2057 Type::Number,
2058 crate::DefaultValue::Number(f64::INFINITY),
2059 ),
2060 ],
2061 ret: Type::Array(Box::new(Type::String)),
2062 predicate: None,
2063 doc: doc(
2064 "/**\n * Split this string into pieces by matches of `separator`. For an empty string `separator`, returns each code unit as a separate part. For a `RegExp` `separator`, each match's captured groups are inserted after the part before it, as in JavaScript (`undefined` for a group that didn't participate).\n * @param separator Substring or pattern to split on.\n * @param limit Optional cap on the number of returned parts (default: no limit).\n */",
2065 ),
2066 },
2067 ),
2068 (
2069 "matchAll".to_string(),
2070 MethodSig {
2071 optional: false,
2072 generics: Vec::new(),
2073 params: vec![Param::new(
2074 "re",
2075 Type::prelude_interface("RegExp".to_string(), Vec::new()),
2076 )],
2077 ret: Type::Array(Box::new(Type::prelude_interface("RegExpMatch".to_string(), Vec::new()))),
2078 predicate: None,
2079 doc: doc(
2080 "/**\n * Find every non-overlapping match of `re` in this string. Zero-length matches advance by one to avoid infinite loops, per ECMA-262.\n * @param re The pattern to match.\n */",
2081 ),
2082 },
2083 ),
2084 ]),
2085 properties: BTreeMap::from([(
2086 "length".to_string(),
2087 PropertySig {
2088 ty: Type::Number,
2089 readonly: true,
2090 intrinsic: true,
2091 optional: false,
2092 doc: doc("/** The number of UTF-16 code units in the string. */"),
2093 },
2094 )]),
2095 dispatch: Dispatch::Direct,
2096 doc: doc("/** The UTF-16 string type. */"),
2097 },
2098 },
2099 );
2100 defs.types.insert(
2101 "StringConstructor".to_string(),
2102 TypeSymbol {
2103 name: "StringConstructor".to_string(),
2104 mangled_name: crate::mangle::prelude("StringConstructor"),
2105 declaration_span: Span::at(crate::FileId::PRELUDE),
2106 kind: TypeKind::Interface { index: None,
2107 generics: Vec::new(),
2108 methods: BTreeMap::from([
2109 (
2110 "@call".to_string(),
2111 MethodSig {
2112 optional: false,
2113 generics: Vec::new(),
2114 params: vec![Param::new("value", Type::Unknown)],
2115 ret: Type::String,
2116 predicate: None,
2117 doc: doc(
2118 "/**\n * Convert any value to its string representation: `\"null\"` and `\"undefined\"` for those values, otherwise the value's `toString()` method.\n */",
2119 ),
2120 },
2121 ),
2122 (
2123 "fromCharCode".to_string(),
2124 MethodSig {
2125 optional: false,
2126 generics: Vec::new(),
2127 params: vec![Param::rest("codes", Type::Array(Box::new(Type::Number)))],
2128 ret: Type::String,
2129 predicate: None,
2130 doc: doc(
2131 "/**\n * Builds a string from UTF-16 code units — one unit per argument.\n * Each value is truncated and reduced modulo 65536, as JavaScript does, so `-1` is U+FFFF.\n * @param codes Zero or more UTF-16 code units (0–65535).\n */",
2132 ),
2133 },
2134 ),
2135 (
2136 "fromCodePoint".to_string(),
2137 MethodSig {
2138 optional: false,
2139 generics: Vec::new(),
2140 params: vec![Param::rest(
2141 "codePoints",
2142 Type::Array(Box::new(Type::Number)),
2143 )],
2144 ret: Type::String,
2145 predicate: None,
2146 doc: doc(
2147 "/**\n * Builds a string from Unicode code points; astral code points (> U+FFFF) encode as surrogate pairs.\n * Throws a `RangeError` on a negative, non-integer, or > U+10FFFF input.\n * @param codePoints Zero or more Unicode code points (0–0x10FFFF).\n */",
2148 ),
2149 },
2150 ),
2151 ]),
2152 properties: BTreeMap::new(),
2153 dispatch: Dispatch::Static,
2154 doc: doc(
2155 "/** Constructor object for `string`. Accessed via the global `String` binding. */",
2156 ),
2157 },
2158 },
2159 );
2160 defs.values.insert(
2161 "String".to_string(),
2162 ValueSymbol {
2163 name: "String".to_string(),
2164 mangled_name: crate::mangle::prelude("String"),
2165 declaration_span: Span::at(crate::FileId::PRELUDE),
2166 kind: ValueKind::Const {
2167 ty: Type::prelude_interface("StringConstructor".to_string(), Vec::new()),
2168 doc: doc(
2169 "/** The `String` constructor — call `String(x)` to convert any value to a string via its `toString()` method. */",
2170 ),
2171 },
2172 },
2173 );
2174}