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 true,
85 |caller, params, results| {
86 let s = read_string_arg(caller, abi_arg(params, 1)?, "TextEncoder#encode")?;
87 let st = write_submilli_uint8array_struct(caller, s.as_bytes())?;
88 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
89 Ok(())
90 },
91 )?;
92 register_host_fn(
93 linker,
94 MODULE_NAME,
95 dec_key("decode"),
96 ft(vec![obj_null.clone(), uint8.clone()], vec![string.clone()]),
97 true,
98 |caller, params, results| {
99 let bytes = read_uint8_array_arg(caller, abi_arg(params, 1)?, "TextDecoder#decode")?;
100 fuel::charge(&mut *caller, fuel::SCAN, bytes.len() as u64)?;
101 let s = std::str::from_utf8(&bytes).map_err(|e| {
102 crate::runtime::host::type_error(format!(
103 "TextDecoder.decode: invalid UTF-8 at byte {}: {e}",
104 e.valid_up_to(),
105 ))
106 })?;
107 let st = write_submilli_string_struct(caller, s)?;
108 *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
109 Ok(())
110 },
111 )?;
112 for key in [enc_ctor_key("new"), dec_ctor_key("new")] {
113 register_host_fn(
114 linker,
115 MODULE_NAME,
116 key,
117 ft(Vec::new(), vec![obj_null.clone()]),
118 true,
119 |caller, _params, results| {
120 *abi_result(results, 0)? = new_instance(caller)?;
121 Ok(())
122 },
123 )?;
124 }
125 Ok(())
126}
127
128pub fn declare(defs: &mut PackageDeclaration) {
129 let encoder = || Type::prelude_interface("TextEncoder".to_string(), Vec::new());
130 let decoder = || Type::prelude_interface("TextDecoder".to_string(), Vec::new());
131 declare_method(
132 defs,
133 "encode",
134 enc_key("encode"),
135 vec![Param::new("self", encoder()), Param::new("s", Type::String)],
136 Type::Uint8Array,
137 );
138 declare_method(
139 defs,
140 "decode",
141 dec_key("decode"),
142 vec![
143 Param::new("self", decoder()),
144 Param::new("bytes", Type::Uint8Array),
145 ],
146 Type::String,
147 );
148 declare_method(defs, "new", enc_ctor_key("new"), Vec::new(), encoder());
149 declare_method(defs, "new", dec_ctor_key("new"), Vec::new(), decoder());
150}
151
152#[allow(clippy::too_many_lines)]
155pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
156 use crate::runtime::prelude::declaration::doc;
157 use crate::{
158 Dispatch, MethodSig, Param, Span, Type, TypeKind, TypeSymbol, ValueKind, ValueSymbol,
159 };
160 use std::collections::BTreeMap;
161 defs.types.insert(
162 "TextEncoder".to_string(),
163 TypeSymbol {
164 name: "TextEncoder".to_string(),
165 mangled_name: crate::mangle::prelude("TextEncoder"),
166 declaration_span: Span::at(crate::FileId::PRELUDE),
167 kind: TypeKind::Interface { index: None,
168 generics: Vec::new(),
169 methods: BTreeMap::from([(
170 "encode".to_string(),
171 MethodSig {
172 optional: false,
173 generics: Vec::new(),
174 params: vec![Param::new("s", Type::String)],
175 ret: Type::Uint8Array,
176 predicate: None,
177 doc: doc(
178 "/**\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 */",
179 ),
180 },
181 )]),
182 properties: BTreeMap::new(),
183 dispatch: Dispatch::Direct,
184 doc: doc(
185 "/** Encodes UTF-16 strings to UTF-8 byte arrays. Mirrors the WHATWG Encoding Standard `TextEncoder`. */",
186 ),
187 },
188 },
189 );
190
191 defs.types.insert(
192 "TextDecoder".to_string(),
193 TypeSymbol {
194 name: "TextDecoder".to_string(),
195 mangled_name: crate::mangle::prelude("TextDecoder"),
196 declaration_span: Span::at(crate::FileId::PRELUDE),
197 kind: TypeKind::Interface { index: None,
198 generics: Vec::new(),
199 methods: BTreeMap::from([(
200 "decode".to_string(),
201 MethodSig {
202 optional: false,
203 generics: Vec::new(),
204 params: vec![Param::new("bytes", Type::Uint8Array)],
205 ret: Type::String,
206 predicate: None,
207 doc: doc(
208 "/**\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 */",
209 ),
210 },
211 )]),
212 properties: BTreeMap::new(),
213 dispatch: Dispatch::Direct,
214 doc: doc(
215 "/** 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). */",
216 ),
217 },
218 },
219 );
220
221 defs.types.insert(
222 "TextEncoderConstructor".to_string(),
223 TypeSymbol {
224 name: "TextEncoderConstructor".to_string(),
225 mangled_name: crate::mangle::prelude("TextEncoderConstructor"),
226 declaration_span: Span::at(crate::FileId::PRELUDE),
227 kind: TypeKind::Interface { index: None,
228 generics: Vec::new(),
229 methods: BTreeMap::from([(
230 "new".to_string(),
231 MethodSig {
232 optional: false,
233 generics: Vec::new(),
234 params: Vec::new(),
235 ret: Type::prelude_interface("TextEncoder".to_string(), Vec::new()),
236 predicate: None,
237 doc: doc(
238 "/** Construct a new `TextEncoder` instance. v1 takes no options (UTF-8 only). */",
239 ),
240 },
241 )]),
242 properties: BTreeMap::new(),
243 dispatch: Dispatch::Static,
244 doc: doc(
245 "/** Constructor object for `TextEncoder`. Accessed via the global `TextEncoder` binding — call `new TextEncoder()`. */",
246 ),
247 },
248 },
249 );
250
251 defs.types.insert(
252 "TextDecoderConstructor".to_string(),
253 TypeSymbol {
254 name: "TextDecoderConstructor".to_string(),
255 mangled_name: crate::mangle::prelude("TextDecoderConstructor"),
256 declaration_span: Span::at(crate::FileId::PRELUDE),
257 kind: TypeKind::Interface { index: None,
258 generics: Vec::new(),
259 methods: BTreeMap::from([(
260 "new".to_string(),
261 MethodSig {
262 optional: false,
263 generics: Vec::new(),
264 params: Vec::new(),
265 ret: Type::prelude_interface("TextDecoder".to_string(), Vec::new()),
266 predicate: None,
267 doc: doc(
268 "/** Construct a new `TextDecoder` instance. v1 takes no options (UTF-8 only; throws on invalid bytes). */",
269 ),
270 },
271 )]),
272 properties: BTreeMap::new(),
273 dispatch: Dispatch::Static,
274 doc: doc(
275 "/** Constructor object for `TextDecoder`. Accessed via the global `TextDecoder` binding — call `new TextDecoder()`. */",
276 ),
277 },
278 },
279 );
280 defs.values.insert(
281 "TextEncoder".to_string(),
282 ValueSymbol {
283 name: "TextEncoder".to_string(),
284 mangled_name: crate::mangle::prelude("TextEncoder"),
285 declaration_span: Span::at(crate::FileId::PRELUDE),
286 kind: ValueKind::Const {
287 ty: Type::prelude_interface("TextEncoderConstructor".to_string(), Vec::new()),
288 doc: doc("/** The `TextEncoder` constructor. */"),
289 },
290 },
291 );
292 defs.values.insert(
293 "TextDecoder".to_string(),
294 ValueSymbol {
295 name: "TextDecoder".to_string(),
296 mangled_name: crate::mangle::prelude("TextDecoder"),
297 declaration_span: Span::at(crate::FileId::PRELUDE),
298 kind: ValueKind::Const {
299 ty: Type::prelude_interface("TextDecoderConstructor".to_string(), Vec::new()),
300 doc: doc("/** The `TextDecoder` constructor. */"),
301 },
302 },
303 );
304}