1use wasm_encoder::{BlockType, Instruction, ValType};
12
13use crate::codegen::CodegenCtx;
14use crate::codegen::function_emitter::{FunctionEmitter, emit_const_string_by_text};
15use crate::runtime::prelude::index::IndexAccess;
16use crate::{ExprId, TypedAst, TypedExprKind};
17
18pub(crate) fn receiver_name(
20 ta: &TypedAst,
21 mut receiver: ExprId,
22) -> Result<String, crate::compiler_error::CompilerFailure> {
23 let mut fields = Vec::new();
24 for _ in 0..8 {
25 let expr = ta
26 .try_expr(receiver)
27 .map_err(crate::codegen::arena_failure)?;
28 let mut name = match &expr.kind {
29 TypedExprKind::LocalRef { ident, .. } if is_identifier(&ident.name) => {
30 ident.name.clone()
31 }
32 TypedExprKind::GlobalRef { name, .. } if is_identifier(&name.name) => name.name.clone(),
33 TypedExprKind::This => "this".to_string(),
34 TypedExprKind::LocalNarrowRef { path, .. }
35 if path.chain.len() <= 8
36 && is_identifier(&path.root.render())
37 && path.chain.iter().all(|segment| {
38 matches!(
39 segment,
40 crate::typechecker::infer::narrowing::PathElem::Field(name)
41 if is_identifier(name)
42 )
43 }) =>
44 {
45 path.render()
46 }
47 TypedExprKind::Narrowed { inner, .. } => {
48 receiver = *inner;
49 continue;
50 }
51 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
52 receiver = *value;
53 continue;
54 }
55 TypedExprKind::FieldAccess {
56 receiver: base,
57 name,
58 }
59 | TypedExprKind::InterfacePropertyAccess {
60 receiver: base,
61 name,
62 ..
63 } => {
64 if !is_identifier(&name.name) {
65 return Ok(String::new());
66 }
67 fields.push(name.name.as_str());
68 receiver = *base;
69 continue;
70 }
71 _ => return Ok(String::new()),
72 };
73 for field in fields.iter().rev() {
74 name.push('.');
75 name.push_str(field);
76 }
77 if name.len() <= 64 {
78 return Ok(name);
79 }
80 return Ok(String::new());
81 }
82 Ok(String::new())
83}
84
85pub(crate) fn chain_receiver_name(
86 ta: &TypedAst,
87 base: ExprId,
88 preceding: &[crate::TypedChainPart],
89) -> Result<String, crate::compiler_error::CompilerFailure> {
90 let mut name = receiver_name(ta, base)?;
91 for part in preceding {
92 if name.is_empty() {
93 break;
94 }
95 match part {
96 crate::TypedChainPart::Field { name: field, .. }
97 | crate::TypedChainPart::InterfaceProperty { name: field, .. } => {
98 if !is_identifier(&field.name)
99 || name
100 .len()
101 .saturating_add(field.name.len())
102 .saturating_add(1)
103 > 64
104 {
105 return Ok(String::new());
106 }
107 name.push('.');
108 name.push_str(&field.name);
109 }
110 crate::TypedChainPart::NonNull { .. } | crate::TypedChainPart::Constraint { .. } => {}
111 _ => return Ok(String::new()),
112 }
113 }
114 Ok(name)
115}
116
117fn is_identifier(name: &str) -> bool {
118 let mut chars = name.chars();
119 chars
120 .next()
121 .is_some_and(|c| c == '_' || c == '$' || unicode_ident::is_xid_start(c))
122 && chars.all(|c| c == '_' || c == '$' || unicode_ident::is_xid_continue(c))
123}
124
125pub fn stash_index_operand(
130 emitter: &mut FunctionEmitter,
131) -> Result<u32, crate::compiler_error::CompilerFailure> {
132 let idx_f64_local = emitter.add_anonymous_local(ValType::F64)?;
133 emitter.instruction(Instruction::LocalSet(idx_f64_local));
134 Ok(idx_f64_local)
135}
136
137pub(crate) fn emit_checked_index(
148 emitter: &mut FunctionEmitter,
149 ctx: &CodegenCtx,
150 raw_arr_local: u32,
151 idx_f64_local: u32,
152 receiver: &str,
153 access: IndexAccess,
154) -> Result<u32, crate::compiler_error::CompilerFailure> {
155 let len = emitter.add_anonymous_local(ValType::I32)?;
156 emitter.instruction(Instruction::LocalGet(raw_arr_local));
157 emitter.instruction(Instruction::ArrayLen);
158 emitter.instruction(Instruction::LocalSet(len));
159 emit_checked_index_with_length(emitter, ctx, len, idx_f64_local, receiver, access)
160}
161
162pub fn stash_array_length(
164 emitter: &mut FunctionEmitter,
165 array_type: u32,
166) -> Result<u32, crate::compiler_error::CompilerFailure> {
167 let receiver = emitter.add_anonymous_local(ValType::Ref(wasm_encoder::RefType {
168 nullable: false,
169 heap_type: wasm_encoder::HeapType::Concrete(array_type),
170 }))?;
171 let len = emitter.add_anonymous_local(ValType::I32)?;
172 emitter.instruction(Instruction::LocalTee(receiver));
173 emitter.instruction(Instruction::StructGet {
174 struct_type_index: array_type,
175 field_index: 2,
176 });
177 emitter.instruction(Instruction::LocalSet(len));
178 emitter.instruction(Instruction::LocalGet(receiver));
179 Ok(len)
180}
181
182pub(crate) fn emit_checked_index_with_length(
183 emitter: &mut FunctionEmitter,
184 ctx: &CodegenCtx,
185 len: u32,
186 idx_f64_local: u32,
187 receiver: &str,
188 access: IndexAccess,
189) -> Result<u32, crate::compiler_error::CompilerFailure> {
190 emit_index_in_range(emitter, len, idx_f64_local);
191 emitter.instruction(Instruction::I32Eqz);
193 emitter.emit_if(BlockType::Empty);
194 emit_index_oob_throw(emitter, ctx, len, idx_f64_local, receiver, access)?;
195 emitter.emit_end();
196
197 let idx_local = emitter.add_anonymous_local(ValType::I32)?;
198 emitter.instruction(Instruction::LocalGet(idx_f64_local));
199 emitter.instruction(Instruction::I32TruncSatF64U);
200 emitter.instruction(Instruction::LocalSet(idx_local));
201 Ok(idx_local)
202}
203
204pub(crate) fn emit_index_in_range(emitter: &mut FunctionEmitter, len: u32, idx_f64_local: u32) {
208 emitter.instruction(Instruction::LocalGet(idx_f64_local));
210 emitter.instruction(Instruction::F64Const(0.0.into()));
211 emitter.instruction(Instruction::F64Ge);
212 emitter.instruction(Instruction::LocalGet(idx_f64_local));
214 emitter.instruction(Instruction::LocalGet(len));
215 emitter.instruction(Instruction::F64ConvertI32U);
216 emitter.instruction(Instruction::F64Lt);
217 emitter.instruction(Instruction::I32And);
218 emitter.instruction(Instruction::LocalGet(idx_f64_local));
220 emitter.instruction(Instruction::LocalGet(idx_f64_local));
221 emitter.instruction(Instruction::F64Floor);
222 emitter.instruction(Instruction::F64Eq);
223 emitter.instruction(Instruction::I32And);
224}
225
226pub(crate) fn emit_group_participation_check(
230 emitter: &mut FunctionEmitter,
231 ctx: &CodegenCtx,
232 len: u32,
233 idx_f64_local: u32,
234 receiver: &str,
235) -> Result<(), crate::compiler_error::CompilerFailure> {
236 let undefined_idx = ctx
237 .symbols
238 .intrinsic_type_indices()
239 .map(|indices| indices.undefined)
240 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic registered"))?;
241 let element = emitter.add_anonymous_local(ctx.symbols.value_type(&crate::Type::Unknown)?)?;
242 emitter.instruction(Instruction::LocalTee(element));
243 emitter.instruction(Instruction::RefTestNonNull(
244 wasm_encoder::HeapType::Concrete(undefined_idx),
245 ));
246 emitter.emit_if(BlockType::Empty);
247 emit_index_oob_throw(
248 emitter,
249 ctx,
250 len,
251 idx_f64_local,
252 receiver,
253 IndexAccess::Group,
254 )?;
255 emitter.emit_end();
256 emitter.instruction(Instruction::LocalGet(element));
257 Ok(())
258}
259
260fn emit_index_oob_throw(
261 emitter: &mut FunctionEmitter,
262 ctx: &CodegenCtx,
263 len: u32,
264 index: u32,
265 receiver: &str,
266 access: IndexAccess,
267) -> Result<(), crate::compiler_error::CompilerFailure> {
268 let helper = ctx
269 .symbols
270 .prelude_func_idx("index_error")
271 .ok_or_else(|| crate::codegen::internal_failure("index error helper imported"))?;
272 emitter.instruction(Instruction::LocalGet(index));
273 emitter.instruction(Instruction::LocalGet(len));
274 emitter.instruction(Instruction::F64ConvertI32U);
275 emit_const_string_by_text(emitter, ctx, receiver)?;
276 emitter.instruction(Instruction::F64Const((access as u32 as f64).into()));
277 emitter.instruction(Instruction::Call(helper));
278 emitter.instruction(Instruction::Unreachable);
280 Ok(())
281}