1use std::collections::BTreeSet;
4
5use wasm_encoder::{
6 ConstExpr, GlobalSection, GlobalType, HeapType, Instruction, RefType, StorageType, ValType,
7};
8
9use crate::Type;
10use crate::codegen::intrinsics::IntrinsicTypeIndices;
11use crate::codegen::symbol_table::SymbolTable;
12use crate::codegen::{internal_failure, next_index, wasm_u32};
13use crate::compiler_error::CompilerFailure;
14
15#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
18pub struct FieldName {
19 pub name: String,
20 pub optional: bool,
21 pub is_accessor: bool,
22 pub is_private: bool,
23}
24
25pub fn declare_optional_name_type(
26 types: &mut wasm_encoder::TypeSection,
27 symbols: &mut SymbolTable,
28 next_type_idx: &mut u32,
29 intrinsics: IntrinsicTypeIndices,
30) -> Result<(), CompilerFailure> {
31 let type_idx = next_index(next_type_idx)?;
32 let mut fields = [intrinsics.vtable, intrinsics.raw_string]
33 .map(|index| wasm_encoder::FieldType {
34 element_type: StorageType::Val(ValType::Ref(RefType {
35 nullable: false,
36 heap_type: HeapType::Concrete(index),
37 })),
38 mutable: false,
39 })
40 .to_vec();
41 fields.push(wasm_encoder::FieldType {
42 element_type: StorageType::Val(ValType::I64),
43 mutable: true,
44 });
45 fields.push(wasm_encoder::FieldType {
46 element_type: StorageType::Val(ValType::I32),
47 mutable: true,
48 });
49 fields.push(wasm_encoder::FieldType {
50 element_type: StorageType::Val(ValType::I32),
51 mutable: false,
52 });
53 types.ty().subtype(&wasm_encoder::SubType {
54 is_final: true,
55 supertype_idx: Some(intrinsics.string),
56 composite_type: wasm_encoder::CompositeType {
57 inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {
58 fields: fields.into(),
59 }),
60 shared: false,
61 descriptor: None,
62 describes: None,
63 },
64 });
65 symbols.record_optional_field_name_type(type_idx);
66 Ok(())
67}
68
69pub fn collect(shapes: &[Type]) -> Vec<Vec<FieldName>> {
70 let mut keys: BTreeSet<Vec<FieldName>> = BTreeSet::new();
71 for shape in shapes {
72 if let Type::Object { fields, .. } = shape {
73 keys.insert(
74 fields
75 .iter()
76 .map(|(name, field)| FieldName {
77 name: name.clone(),
78 optional: field.optional,
79 is_accessor: false,
80 is_private: false,
81 })
82 .collect(),
83 );
84 }
85 }
86 keys.into_iter().collect()
87}
88
89pub fn emit(
90 shapes: &[Vec<FieldName>],
91 globals: &mut GlobalSection,
92 symbols: &mut SymbolTable,
93 next_global_idx: &mut u32,
94 intrinsics: IntrinsicTypeIndices,
95 string_vtable_global_idx: u32,
96) -> Result<(), CompilerFailure> {
97 let field_names_val_type = ValType::Ref(RefType {
98 nullable: false,
99 heap_type: HeapType::Concrete(intrinsics.field_names),
100 });
101 let optional_name_type = symbols.optional_field_name_type()?;
102 for shape in shapes {
103 let init = build_init_expr(
104 shape,
105 intrinsics,
106 string_vtable_global_idx,
107 optional_name_type,
108 )?;
109 let global_idx = next_index(next_global_idx)?;
110 globals.global(
111 GlobalType {
112 val_type: field_names_val_type,
113 mutable: false,
114 shared: false,
115 },
116 &init,
117 );
118 symbols.record_field_names_global(shape.clone(), global_idx);
119 }
120 Ok(())
121}
122
123fn build_init_expr(
124 shape: &[FieldName],
125 intrinsics: IntrinsicTypeIndices,
126 string_vtable_global_idx: u32,
127 optional_name_type: u32,
128) -> Result<ConstExpr, CompilerFailure> {
129 let mut instrs: Vec<Instruction<'_>> = Vec::new();
130 for name in shape {
131 let code_units = crate::literal_units::literal_units(&name.name);
132 instrs.push(Instruction::GlobalGet(string_vtable_global_idx));
133 for unit in &code_units {
134 instrs.push(Instruction::I32Const(i32::from(*unit)));
135 }
136 instrs.push(Instruction::ArrayNewFixed {
137 array_type_index: intrinsics.raw_string,
138 array_size: wasm_u32(code_units.len())?,
139 });
140 instrs.push(Instruction::I64Const(0));
141 if name.optional || name.is_accessor || name.is_private {
142 instrs.push(Instruction::I32Const(if name.is_accessor {
143 -1
144 } else if name.optional {
145 0
146 } else {
147 1
148 }));
149 instrs.push(Instruction::I32Const(i32::from(name.is_private)));
150 }
151 instrs.push(Instruction::StructNew(
152 if name.optional || name.is_accessor || name.is_private {
153 optional_name_type
154 } else {
155 intrinsics.string
156 },
157 ));
158 }
159 instrs.push(Instruction::ArrayNewFixed {
160 array_type_index: intrinsics.field_names,
161 array_size: wasm_u32(shape.len())?,
162 });
163 Ok(ConstExpr::extended(instrs))
164}
165
166pub(crate) fn emit_instance_names(
169 emitter: &mut super::function_emitter::FunctionEmitter,
170 ctx: &super::CodegenCtx,
171 names: &[FieldName],
172 present: impl Fn(&str) -> bool,
173) -> Result<(), CompilerFailure> {
174 let global = ctx
175 .symbols
176 .field_names_global_idx(names)
177 .ok_or_else(|| internal_failure("object field names were not collected"))?;
178 if !names.iter().any(|name| name.optional) {
179 emitter.instruction(Instruction::GlobalGet(global));
180 return Ok(());
181 }
182 let intrinsics = intrinsics(ctx)?;
183 let optional_name_type = ctx.symbols.optional_field_name_type()?;
184 for (index, name) in names.iter().enumerate() {
185 let index = wasm_u32(index)?.cast_signed();
186 if name.optional {
187 for field_index in [0, 1] {
188 emitter.instruction(Instruction::GlobalGet(global));
189 emitter.instruction(Instruction::I32Const(index));
190 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
191 emitter.instruction(Instruction::StructGet {
192 struct_type_index: intrinsics.string,
193 field_index,
194 });
195 }
196 emitter.instruction(Instruction::I64Const(0));
197 emitter.instruction(Instruction::I32Const(i32::from(present(&name.name))));
198 emitter.instruction(Instruction::I32Const(i32::from(name.is_private)));
199 emitter.instruction(Instruction::StructNew(optional_name_type));
200 } else {
201 emitter.instruction(Instruction::GlobalGet(global));
202 emitter.instruction(Instruction::I32Const(index));
203 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
204 }
205 }
206 emitter.instruction(Instruction::ArrayNewFixed {
207 array_type_index: intrinsics.field_names,
208 array_size: wasm_u32(names.len())?,
209 });
210 Ok(())
211}
212
213pub(crate) fn emit_name_is_accessor(
215 emitter: &mut super::function_emitter::FunctionEmitter,
216 ctx: &super::CodegenCtx,
217) -> Result<(), crate::compiler_error::CompilerFailure> {
218 let name = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
219 let marked = ctx.symbols.optional_field_name_type()?;
220 emitter.instruction(Instruction::LocalTee(name));
221 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(marked)));
222 emitter.emit_if(wasm_encoder::BlockType::Result(ValType::I32));
223 emitter.instruction(Instruction::LocalGet(name));
224 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(marked)));
225 emitter.instruction(Instruction::StructGet {
226 struct_type_index: marked,
227 field_index: 3,
228 });
229 emitter.instruction(Instruction::I32Const(-1));
230 emitter.instruction(Instruction::I32Eq);
231 emitter.emit_else();
232 emitter.instruction(Instruction::I32Const(0));
233 emitter.emit_end();
234
235 Ok(())
236}
237
238pub(crate) fn emit_name_presence(
240 emitter: &mut super::function_emitter::FunctionEmitter,
241 ctx: &super::CodegenCtx,
242) -> Result<(), CompilerFailure> {
243 let intrinsics = intrinsics(ctx)?;
244 let optional = ctx.symbols.optional_field_name_type()?;
245 let name = emitter.add_anonymous_local(ValType::Ref(RefType {
246 nullable: false,
247 heap_type: HeapType::Concrete(intrinsics.string),
248 }))?;
249 emitter.instruction(Instruction::LocalTee(name));
250 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(optional)));
251 emitter.emit_if(wasm_encoder::BlockType::Result(ValType::I32));
252 emitter.instruction(Instruction::LocalGet(name));
253 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(optional)));
254 emitter.instruction(Instruction::StructGet {
255 struct_type_index: optional,
256 field_index: 3,
257 });
258 emitter.instruction(Instruction::I32Eqz);
259 emitter.instruction(Instruction::I32Eqz);
260 emitter.emit_else();
261 emitter.instruction(Instruction::I32Const(1));
262 emitter.emit_end();
263 Ok(())
264}
265
266pub(crate) fn emit_mark_present(
268 emitter: &mut super::function_emitter::FunctionEmitter,
269 ctx: &super::CodegenCtx,
270 object: u32,
271 index: u32,
272) -> Result<(), CompilerFailure> {
273 let intrinsics = intrinsics(ctx)?;
274 let optional = ctx.symbols.optional_field_name_type()?;
275 let name = emitter.add_anonymous_local(ValType::Ref(RefType {
276 nullable: false,
277 heap_type: HeapType::Concrete(intrinsics.string),
278 }))?;
279 emitter.instruction(Instruction::LocalGet(object));
280 emitter.instruction(Instruction::StructGet {
281 struct_type_index: intrinsics.object_shape,
282 field_index: 1,
283 });
284 emitter.instruction(Instruction::LocalGet(index));
285 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
286 emitter.instruction(Instruction::LocalTee(name));
287 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(optional)));
288 emitter.emit_if(wasm_encoder::BlockType::Empty);
289 emitter.instruction(Instruction::LocalGet(name));
290 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(optional)));
291 emitter.instruction(Instruction::StructGet {
292 struct_type_index: optional,
293 field_index: 3,
294 });
295 emitter.instruction(Instruction::I32Eqz);
296 emitter.emit_if(wasm_encoder::BlockType::Empty);
297 emitter.instruction(Instruction::LocalGet(name));
298 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(optional)));
299 emitter.instruction(Instruction::I32Const(1));
300 emitter.instruction(Instruction::StructSet {
301 struct_type_index: optional,
302 field_index: 3,
303 });
304 emitter.emit_end();
305 emitter.emit_end();
306 Ok(())
307}
308
309fn intrinsics(ctx: &super::CodegenCtx) -> Result<IntrinsicTypeIndices, CompilerFailure> {
310 ctx.symbols
311 .intrinsic_type_indices()
312 .ok_or_else(|| internal_failure("intrinsic types are not declared"))
313}
314
315pub(crate) fn emit_optional_presence(
317 function: &mut wasm_encoder::Function,
318 intrinsics: IntrinsicTypeIndices,
319 optional_name_type: u32,
320 object: u32,
321 index: u32,
322 value: u32,
323) {
324 for instruction in [
325 Instruction::LocalGet(object),
326 Instruction::RefCastNonNull(HeapType::Concrete(intrinsics.object_shape)),
327 Instruction::StructGet {
328 struct_type_index: intrinsics.object_shape,
329 field_index: 1,
330 },
331 Instruction::I32Const(index.cast_signed()),
332 Instruction::ArrayGet(intrinsics.field_names),
333 Instruction::RefCastNonNull(HeapType::Concrete(optional_name_type)),
334 Instruction::StructGet {
335 struct_type_index: optional_name_type,
336 field_index: 3,
337 },
338 ]
339 .into_iter()
340 .chain(super::nullish::is_nullish(
341 &[Instruction::LocalGet(value)],
342 intrinsics,
343 ))
344 .chain([Instruction::I32Eqz, Instruction::I32Or])
345 {
346 function.instruction(&instruction);
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use std::collections::BTreeMap;
354
355 #[test]
356 fn collect_dedupes_same_shape_with_different_field_types() {
357 use crate::ObjectField;
358 let mut a = BTreeMap::new();
359 a.insert("x".to_string(), ObjectField::required(Type::Number));
360 let mut b = BTreeMap::new();
361 b.insert("x".to_string(), ObjectField::required(Type::String));
362 let shapes = vec![
363 Type::Object {
364 index: None,
365 fields: a,
366 },
367 Type::Object {
368 index: None,
369 fields: b,
370 },
371 ];
372 let keys = collect(&shapes);
373 assert_eq!(keys.len(), 1);
374 assert_eq!(
375 keys[0],
376 vec![FieldName {
377 name: "x".to_string(),
378 optional: false,
379 is_accessor: false,
380 is_private: false,
381 }]
382 );
383 }
384
385 #[test]
386 fn collect_separates_shapes_with_different_field_names() {
387 use crate::ObjectField;
388 let mut a = BTreeMap::new();
389 a.insert("x".to_string(), ObjectField::required(Type::Number));
390 let mut b = BTreeMap::new();
391 b.insert("y".to_string(), ObjectField::required(Type::Number));
392 let shapes = vec![
393 Type::Object {
394 index: None,
395 fields: a,
396 },
397 Type::Object {
398 index: None,
399 fields: b,
400 },
401 ];
402 let keys = collect(&shapes);
403 assert_eq!(keys.len(), 2);
404 assert_eq!(
405 keys[0],
406 vec![FieldName {
407 name: "x".to_string(),
408 optional: false,
409 is_accessor: false,
410 is_private: false,
411 }]
412 );
413 assert_eq!(
414 keys[1],
415 vec![FieldName {
416 name: "y".to_string(),
417 optional: false,
418 is_accessor: false,
419 is_private: false,
420 }]
421 );
422 }
423}