1use super::arg_str;
7use crate::host::{call_method, invoke, with_host, JsObj};
8use fusevm::Value;
9use indexmap::IndexMap;
10
11pub fn new_emitter() -> Value {
13 with_host(|h| {
14 let on = h.new_object(IndexMap::new());
15 let once = h.new_object(IndexMap::new());
16 let mut m = IndexMap::new();
17 m.insert("@@native".into(), h.new_str("EventEmitter"));
18 m.insert("@@on".into(), on);
19 m.insert("@@once".into(), once);
20 h.new_object(m)
21 })
22}
23
24pub const METHODS: &[&str] = &[
28 "on",
29 "addListener",
30 "prependListener",
31 "once",
32 "prependOnceListener",
33 "emit",
34 "removeListener",
35 "off",
36 "removeAllListeners",
37 "listenerCount",
38 "listeners",
39 "eventNames",
40 "setMaxListeners",
41 "getMaxListeners",
42];
43
44pub fn instance_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
45 match method {
46 "listeners" => Ok(with_host(|h| {
47 h.new_array(listeners(recv, &arg_str(&args, 0)))
48 })),
49 "setMaxListeners" => Ok(recv.clone()),
51 "getMaxListeners" => Ok(Value::Float(10.0)),
52 "on" | "addListener" | "prependListener" => {
53 add(
54 recv,
55 "@@on",
56 &arg_str(&args, 0),
57 args.get(1).cloned().unwrap_or(Value::Undef),
58 );
59 Ok(recv.clone())
60 }
61 "once" | "prependOnceListener" => {
62 add(
63 recv,
64 "@@once",
65 &arg_str(&args, 0),
66 args.get(1).cloned().unwrap_or(Value::Undef),
67 );
68 Ok(recv.clone())
69 }
70 "emit" => emit(
71 recv,
72 &arg_str(&args, 0),
73 &args.get(1..).map(|s| s.to_vec()).unwrap_or_default(),
74 ),
75 "removeListener" | "off" => {
76 remove(recv, &arg_str(&args, 0), args.get(1).cloned());
77 Ok(recv.clone())
78 }
79 "removeAllListeners" => {
80 let name = if args.is_empty() {
81 None
82 } else {
83 Some(arg_str(&args, 0))
84 };
85 remove_all(recv, name.as_deref());
86 Ok(recv.clone())
87 }
88 "listenerCount" => Ok(Value::Float(
89 listeners(recv, &arg_str(&args, 0)).len() as f64
90 )),
91 "eventNames" => Ok(with_host(|h| {
92 let mut keys: Vec<String> = Vec::new();
93 for map in ["@@on", "@@once"] {
94 if let Some(JsObj::Object(p)) = named_map(h, recv, map).and_then(|v| h.get(&v)) {
95 keys.extend(p.keys().cloned());
96 }
97 }
98 let names: Vec<Value> = keys.into_iter().map(|k| h.new_str(k)).collect();
99 h.new_array(names)
100 })),
101 _ => Err(crate::host::type_error(&format!(
102 "emitter.{method} is not a function"
103 ))),
104 }
105}
106
107fn named_map(h: &crate::host::JsHost, recv: &Value, which: &str) -> Option<Value> {
111 match h.get(recv) {
112 Some(JsObj::Object(p)) => p.get(which).cloned(),
113 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(recv, which),
114 _ => None,
115 }
116}
117
118fn set_named_map(h: &mut crate::host::JsHost, recv: &Value, which: &str, val: Value) {
120 match h.get(recv) {
121 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.set_fn_prop(recv, which, val),
122 _ => {
123 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
124 p.insert(which.to_string(), val);
125 }
126 }
127 }
128}
129
130fn add(recv: &Value, which: &str, name: &str, f: Value) {
131 with_host(|h| {
132 let map = match named_map(h, recv, which) {
134 Some(m) => m,
135 None => {
136 let m = h.new_object(IndexMap::new());
137 set_named_map(h, recv, which, m.clone());
138 m
139 }
140 };
141 let arr = match h.get(&map) {
143 Some(JsObj::Object(p)) => p.get(name).cloned(),
144 _ => None,
145 };
146 let arr = match arr {
147 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
148 _ => {
149 let a = h.new_array(Vec::new());
150 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
151 p.insert(name.to_string(), a.clone());
152 }
153 a
154 }
155 };
156 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
157 items.push(f);
158 }
159 });
160}
161
162fn listeners(recv: &Value, name: &str) -> Vec<Value> {
163 with_host(|h| {
164 let mut out = Vec::new();
165 for which in ["@@on", "@@once"] {
166 if let Some(map) = named_map(h, recv, which) {
167 if let Some(JsObj::Object(p)) = h.get(&map) {
168 if let Some(a) = p.get(name) {
169 if let Some(JsObj::Array(items)) = h.get(a) {
170 out.extend(items.iter().cloned());
171 }
172 }
173 }
174 }
175 }
176 out
177 })
178}
179
180fn emit(recv: &Value, name: &str, args: &[Value]) -> Result<Value, String> {
181 let to_call = listeners(recv, name);
182 remove_all_of(recv, "@@once", Some(name));
184 let had = !to_call.is_empty();
185 for f in to_call {
186 invoke(&f, args.to_vec(), Some(recv.clone()))?;
187 }
188 resolve_waiters(recv, name, args);
190 Ok(Value::Bool(had))
191}
192
193fn add_waiter(recv: &Value, name: &str, promise: Value) {
203 with_host(|h| {
204 let map = match waiter_map(h, recv) {
205 Some(m) => m,
206 None => {
207 let m = h.new_object(IndexMap::new());
208 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
209 p.insert("@@waiters".into(), m.clone());
210 }
211 m
212 }
213 };
214 let existing = match h.get(&map) {
215 Some(JsObj::Object(p)) => p.get(name).cloned(),
216 _ => None,
217 };
218 let arr = match existing {
219 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
220 _ => {
221 let a = h.new_array(Vec::new());
222 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
223 p.insert(name.to_string(), a.clone());
224 }
225 a
226 }
227 };
228 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
229 items.push(promise);
230 }
231 });
232}
233
234fn waiter_map(h: &crate::host::JsHost, recv: &Value) -> Option<Value> {
235 match h.get(recv) {
236 Some(JsObj::Object(p)) => p.get("@@waiters").cloned(),
237 _ => None,
238 }
239}
240
241fn take_waiters(recv: &Value, name: &str) -> Vec<Value> {
243 with_host(|h| {
244 let Some(map) = waiter_map(h, recv) else {
245 return Vec::new();
246 };
247 let arr = match h.get_mut(&map) {
248 Some(JsObj::Object(p)) => p.shift_remove(name),
249 _ => None,
250 };
251 let Some(arr) = arr else { return Vec::new() };
252 match h.get(&arr) {
253 Some(JsObj::Array(items)) => items.clone(),
254 _ => Vec::new(),
255 }
256 })
257}
258
259fn take_waiters_except(recv: &Value, keep: &str) -> Vec<Value> {
261 with_host(|h| {
262 let Some(map) = waiter_map(h, recv) else {
263 return Vec::new();
264 };
265 let keys: Vec<String> = match h.get(&map) {
266 Some(JsObj::Object(p)) => p.keys().filter(|k| k.as_str() != keep).cloned().collect(),
267 _ => Vec::new(),
268 };
269 let mut out = Vec::new();
270 for k in keys {
271 let arr = match h.get_mut(&map) {
272 Some(JsObj::Object(p)) => p.shift_remove(&k),
273 _ => None,
274 };
275 if let Some(arr) = arr {
276 if let Some(JsObj::Array(items)) = h.get(&arr) {
277 out.extend(items.iter().cloned());
278 }
279 }
280 }
281 out
282 })
283}
284
285fn resolve_waiters(recv: &Value, name: &str, args: &[Value]) {
286 let waiting = take_waiters(recv, name);
287 if !waiting.is_empty() {
288 let arr = with_host(|h| h.new_array(args.to_vec()));
289 for p in &waiting {
290 if let Some(id) = with_host(|h| h.promise_id(p)) {
291 crate::host::resolve_promise_val(id, arr.clone());
292 }
293 }
294 }
295 if name == "error" {
296 let err = args.first().cloned().unwrap_or(Value::Undef);
297 for p in take_waiters_except(recv, "error") {
298 if let Some(id) = with_host(|h| h.promise_id(&p)) {
299 crate::host::reject_promise_val(id, err.clone());
300 }
301 }
302 }
303}
304
305pub const STATIC_METHODS: &[&str] = &[
311 "once",
312 "listenerCount",
313 "getEventListeners",
314 "getMaxListeners",
315 "setMaxListeners",
316 "addAbortListener",
317 "init",
318];
319
320pub fn static_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
323 let emitter = args.first().cloned().unwrap_or(Value::Undef);
324 Some(match method {
325 "once" => Ok(once_static(emitter, &arg_str(args, 1))),
326 "listenerCount" => Ok(Value::Float(
327 listeners(&emitter, &arg_str(args, 1)).len() as f64
328 )),
329 "getEventListeners" => Ok(with_host(|h| {
330 h.new_array(listeners(&emitter, &arg_str(args, 1)))
331 })),
332 "getMaxListeners" => Ok(Value::Float(10.0)),
334 "setMaxListeners" => Ok(Value::Undef),
335 "addAbortListener" => Ok(add_abort_listener(args)),
336 "init" => Ok(init_emitter(emitter)),
337 _ => return None,
338 })
339}
340
341fn once_static(emitter: Value, name: &str) -> Value {
344 let p = with_host(|h| h.new_promise());
345 add_waiter(&emitter, name, p.clone());
346 p
347}
348
349fn init_emitter(emitter: Value) -> Value {
352 with_host(|h| {
353 let has = matches!(h.get(&emitter), Some(JsObj::Object(p)) if p.contains_key("@@on"));
354 if !has {
355 let on = h.new_object(IndexMap::new());
356 let once = h.new_object(IndexMap::new());
357 let native = h.new_str("EventEmitter");
358 if let Some(JsObj::Object(p)) = h.get_mut(&emitter) {
359 p.entry("@@native".to_string()).or_insert(native);
360 p.insert("@@on".to_string(), on);
361 p.insert("@@once".to_string(), once);
362 }
363 }
364 });
365 emitter
366}
367
368fn add_abort_listener(args: &[Value]) -> Value {
373 let signal = args.first().cloned().unwrap_or(Value::Undef);
374 let listener = args.get(1).cloned().unwrap_or(Value::Undef);
375 let name = with_host(|h| h.new_str("abort"));
376 let _ = call_method(&signal, "once", vec![name, listener]);
377 with_host(|h| h.new_object(IndexMap::new()))
378}
379
380fn remove(recv: &Value, name: &str, f: Option<Value>) {
381 let Some(f) = f else { return };
382 with_host(|h| {
383 for which in ["@@on", "@@once"] {
384 if let Some(map) = named_map(h, recv, which) {
385 let arr = match h.get(&map) {
386 Some(JsObj::Object(p)) => p.get(name).cloned(),
387 _ => None,
388 };
389 if let Some(a) = arr {
390 let now_empty = if let Some(JsObj::Array(items)) = h.get_mut(&a) {
391 if let Some(pos) = items.iter().position(|x| x == &f) {
392 items.remove(pos);
393 }
394 items.is_empty()
395 } else {
396 false
397 };
398 if now_empty {
401 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
402 p.shift_remove(name);
403 }
404 }
405 }
406 }
407 }
408 });
409}
410
411fn remove_all(recv: &Value, name: Option<&str>) {
412 remove_all_of(recv, "@@on", name);
413 remove_all_of(recv, "@@once", name);
414}
415
416fn remove_all_of(recv: &Value, which: &str, name: Option<&str>) {
417 with_host(|h| {
418 if let Some(map) = named_map(h, recv, which) {
419 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
420 match name {
421 Some(n) => {
422 p.shift_remove(n);
423 }
424 None => p.clear(),
425 }
426 }
427 }
428 });
429}