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