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