1use crate::runtime::host::{abi_arg, abi_result};
8use wasmtime::{
9 ArrayRef, ArrayRefPre, Caller, FuncType, HeapType, Linker, RefType, StructRef, StructRefPre,
10 Val, ValType,
11};
12
13use crate::runtime::StoreData;
14use crate::runtime::fuel;
15use crate::runtime::host::{
16 host_object_vtable, intrinsic_string_type, intrinsic_uint8_array_type, read_string_arg,
17 read_uint8_array_arg, register_host_fn, write_submilli_string_struct,
18 write_submilli_uint8array_struct,
19};
20use crate::runtime::intrinsic_types::{build_intrinsic_types, intrinsic_types};
21use crate::runtime::prelude::{MODULE_NAME, declare_method};
22use crate::{MangledName, PackageDeclaration, Param, Type};
23
24fn enc_key(method: &str) -> MangledName {
25 crate::mangle::extend(&crate::mangle::prelude("TextEncoder"), method)
26}
27
28fn dec_key(method: &str) -> MangledName {
29 crate::mangle::extend(&crate::mangle::prelude("TextDecoder"), method)
30}
31
32fn enc_ctor_key(method: &str) -> MangledName {
33 crate::mangle::extend(&crate::mangle::prelude("TextEncoderConstructor"), method)
34}
35
36fn dec_ctor_key(method: &str) -> MangledName {
37 crate::mangle::extend(&crate::mangle::prelude("TextDecoderConstructor"), method)
38}
39
40fn ref_to(struct_ty: wasmtime::StructType) -> ValType {
41 ValType::Ref(RefType::new(false, HeapType::ConcreteStruct(struct_ty)))
42}
43
44fn new_instance(caller: &mut Caller<'_, StoreData>) -> wasmtime::Result<Val> {
46 let intr = intrinsic_types(&mut *caller)?;
47 let vtable = host_object_vtable(caller)?;
48 let names = {
49 let pre = ArrayRefPre::new(&mut *caller, intr.field_names.clone());
50 let arr = ArrayRef::new_fixed(&mut *caller, &pre, &[])?;
51 Val::AnyRef(Some(arr.to_anyref()))
52 };
53 let fields = {
54 let pre = ArrayRefPre::new(&mut *caller, intr.object_fields.clone());
55 let arr = ArrayRef::new_fixed(&mut *caller, &pre, &[])?;
56 Val::AnyRef(Some(arr.to_anyref()))
57 };
58 let pre = StructRefPre::new(&mut *caller, intr.object_shape.clone());
59 let st = StructRef::new(
60 &mut *caller,
61 &pre,
62 &[vtable, names, fields, Val::AnyRef(None)],
63 )?;
64 Ok(Val::AnyRef(Some(st.to_anyref())))
65}
66
67pub fn install(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
68 let engine = linker.engine().clone();
69 let intr = build_intrinsic_types(&engine)?;
70 let obj_null = ValType::Ref(RefType::new(
71 true,
72 HeapType::ConcreteStruct(intr.object.clone()),
73 ));
74 let string = ref_to(intrinsic_string_type(&engine)?);
75 let uint8 = ref_to(intrinsic_uint8_array_type(&engine)?);
76
77 let ft = |params: Vec<ValType>, results: Vec<ValType>| FuncType::new(&engine, params, results);
78
79 register_host_fn(
80 linker,
81 MODULE_NAME,
82 enc_key("encode"),
83 ft(vec![obj_null.clone(), string.clone()], vec![uint8.clone()]),
84 |caller, params, results| {
85 let s = read_string_arg(caller, abi_arg(params, 1)?, "TextEncoder#encode")?;
86 let st = write_submilli_uint8array_struct(caller, s.as_bytes())?;
87 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
88 Ok(())
89 },
90 )?;
91 register_host_fn(
92 linker,
93 MODULE_NAME,
94 dec_key("decode"),
95 ft(vec![obj_null.clone(), uint8.clone()], vec![string.clone()]),
96 |caller, params, results| {
97 let bytes = read_uint8_array_arg(caller, abi_arg(params, 1)?, "TextDecoder#decode")?;
98 fuel::charge(&mut *caller, fuel::SCAN, bytes.len() as u64)?;
99 let s = std::str::from_utf8(&bytes).map_err(|e| {
100 crate::runtime::host::type_error(format!(
101 "TextDecoder.decode: invalid UTF-8 at byte {}: {e}",
102 e.valid_up_to(),
103 ))
104 })?;
105 let st = write_submilli_string_struct(caller, s)?;
106 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
107 Ok(())
108 },
109 )?;
110 for key in [enc_ctor_key("new"), dec_ctor_key("new")] {
111 register_host_fn(
112 linker,
113 MODULE_NAME,
114 key,
115 ft(Vec::new(), vec![obj_null.clone()]),
116 |caller, _params, results| {
117 *abi_result(results, 0)? = new_instance(caller)?;
118 Ok(())
119 },
120 )?;
121 }
122 Ok(())
123}
124
125pub fn declare(defs: &mut PackageDeclaration) {
126 let encoder = || Type::prelude_interface("TextEncoder".to_string(), Vec::new());
127 let decoder = || Type::prelude_interface("TextDecoder".to_string(), Vec::new());
128 declare_method(
129 defs,
130 "encode",
131 enc_key("encode"),
132 vec![Param::new("self", encoder()), Param::new("s", Type::String)],
133 Type::Uint8Array,
134 );
135 declare_method(
136 defs,
137 "decode",
138 dec_key("decode"),
139 vec![
140 Param::new("self", decoder()),
141 Param::new("bytes", Type::Uint8Array),
142 ],
143 Type::String,
144 );
145 declare_method(defs, "new", enc_ctor_key("new"), Vec::new(), encoder());
146 declare_method(defs, "new", dec_ctor_key("new"), Vec::new(), decoder());
147}
148
149#[allow(clippy::too_many_lines)]
152pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
153 use crate::runtime::prelude::declaration::doc;
154 use crate::{
155 Dispatch, MethodSig, Param, Span, Type, TypeKind, TypeSymbol, ValueKind, ValueSymbol,
156 };
157 use std::collections::BTreeMap;
158 defs.types.insert(
159 "TextEncoder".to_string(),
160 TypeSymbol {
161 name: "TextEncoder".to_string(),
162 mangled_name: crate::mangle::prelude("TextEncoder"),
163 declaration_span: Span::at(crate::FileId::PRELUDE),
164 kind: TypeKind::Interface { index: None,
165 generics: Vec::new(),
166 methods: BTreeMap::from([(
167 "encode".to_string(),
168 MethodSig {
169 optional: false,
170 generics: Vec::new(),
171 params: vec![Param::new("s", Type::String)],
172 ret: Type::Uint8Array,
173 predicate: None,
174 doc: doc(
175 "/**\n * UTF-8 encode `s` into a `Uint8Array`. Strings are UTF-16 internally; each Unicode code point becomes one to four UTF-8 bytes.\n * @param s The string to encode.\n */",
176 ),
177 },
178 )]),
179 properties: BTreeMap::new(),
180 dispatch: Dispatch::Direct,
181 doc: doc(
182 "/** Encodes UTF-16 strings to UTF-8 byte arrays. Mirrors the WHATWG Encoding Standard `TextEncoder`. */",
183 ),
184 },
185 },
186 );
187
188 defs.types.insert(
189 "TextDecoder".to_string(),
190 TypeSymbol {
191 name: "TextDecoder".to_string(),
192 mangled_name: crate::mangle::prelude("TextDecoder"),
193 declaration_span: Span::at(crate::FileId::PRELUDE),
194 kind: TypeKind::Interface { index: None,
195 generics: Vec::new(),
196 methods: BTreeMap::from([(
197 "decode".to_string(),
198 MethodSig {
199 optional: false,
200 generics: Vec::new(),
201 params: vec![Param::new("bytes", Type::Uint8Array)],
202 ret: Type::String,
203 predicate: None,
204 doc: doc(
205 "/**\n * Decode `bytes` as UTF-8 into a UTF-16 string. Throws a catchable `TypeError` on invalid UTF-8.\n * @param bytes The bytes to decode.\n */",
206 ),
207 },
208 )]),
209 properties: BTreeMap::new(),
210 dispatch: Dispatch::Direct,
211 doc: doc(
212 "/** Decodes UTF-8 byte arrays into UTF-16 strings. Mirrors the WHATWG Encoding Standard `TextDecoder`. UTF-8 only in v1 (no `encoding` / `fatal` / `stream` options). */",
213 ),
214 },
215 },
216 );
217
218 defs.types.insert(
219 "TextEncoderConstructor".to_string(),
220 TypeSymbol {
221 name: "TextEncoderConstructor".to_string(),
222 mangled_name: crate::mangle::prelude("TextEncoderConstructor"),
223 declaration_span: Span::at(crate::FileId::PRELUDE),
224 kind: TypeKind::Interface { index: None,
225 generics: Vec::new(),
226 methods: BTreeMap::from([(
227 "new".to_string(),
228 MethodSig {
229 optional: false,
230 generics: Vec::new(),
231 params: Vec::new(),
232 ret: Type::prelude_interface("TextEncoder".to_string(), Vec::new()),
233 predicate: None,
234 doc: doc(
235 "/** Construct a new `TextEncoder` instance. v1 takes no options (UTF-8 only). */",
236 ),
237 },
238 )]),
239 properties: BTreeMap::new(),
240 dispatch: Dispatch::Static,
241 doc: doc(
242 "/** Constructor object for `TextEncoder`. Accessed via the global `TextEncoder` binding — call `new TextEncoder()`. */",
243 ),
244 },
245 },
246 );
247
248 defs.types.insert(
249 "TextDecoderConstructor".to_string(),
250 TypeSymbol {
251 name: "TextDecoderConstructor".to_string(),
252 mangled_name: crate::mangle::prelude("TextDecoderConstructor"),
253 declaration_span: Span::at(crate::FileId::PRELUDE),
254 kind: TypeKind::Interface { index: None,
255 generics: Vec::new(),
256 methods: BTreeMap::from([(
257 "new".to_string(),
258 MethodSig {
259 optional: false,
260 generics: Vec::new(),
261 params: Vec::new(),
262 ret: Type::prelude_interface("TextDecoder".to_string(), Vec::new()),
263 predicate: None,
264 doc: doc(
265 "/** Construct a new `TextDecoder` instance. v1 takes no options (UTF-8 only; throws on invalid bytes). */",
266 ),
267 },
268 )]),
269 properties: BTreeMap::new(),
270 dispatch: Dispatch::Static,
271 doc: doc(
272 "/** Constructor object for `TextDecoder`. Accessed via the global `TextDecoder` binding — call `new TextDecoder()`. */",
273 ),
274 },
275 },
276 );
277 defs.values.insert(
278 "TextEncoder".to_string(),
279 ValueSymbol {
280 name: "TextEncoder".to_string(),
281 mangled_name: crate::mangle::prelude("TextEncoder"),
282 declaration_span: Span::at(crate::FileId::PRELUDE),
283 kind: ValueKind::Const {
284 ty: Type::prelude_interface("TextEncoderConstructor".to_string(), Vec::new()),
285 doc: doc("/** The `TextEncoder` constructor. */"),
286 },
287 },
288 );
289 defs.values.insert(
290 "TextDecoder".to_string(),
291 ValueSymbol {
292 name: "TextDecoder".to_string(),
293 mangled_name: crate::mangle::prelude("TextDecoder"),
294 declaration_span: Span::at(crate::FileId::PRELUDE),
295 kind: ValueKind::Const {
296 ty: Type::prelude_interface("TextDecoderConstructor".to_string(), Vec::new()),
297 doc: doc("/** The `TextDecoder` constructor. */"),
298 },
299 },
300 );
301}