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 "rawListeners",
40 "eventNames",
41 "setMaxListeners",
42 "getMaxListeners",
43];
44
45fn event_key(args: &[Value]) -> String {
52 with_host(|h| h.property_key(args.first().unwrap_or(&Value::Undef)))
53}
54
55pub fn instance_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
56 match method {
57 "listeners" | "rawListeners" => {
64 let items = listeners(recv, &event_key(&args));
65 Ok(with_host(|h| h.new_array(items)))
66 }
67 "setMaxListeners" => {
71 let n = args
72 .first()
73 .map(|v| with_host(|h| h.to_number(v)))
74 .unwrap_or(10.0);
75 with_host(|h| {
76 let nv = Value::Float(n);
77 match h.get_mut(recv) {
78 Some(JsObj::Object(p)) => {
79 p.insert("@@maxListeners".into(), nv);
80 }
81 _ => h.set_fn_prop(recv, "@@maxListeners", nv),
82 }
83 });
84 Ok(recv.clone())
85 }
86 "getMaxListeners" => Ok(with_host(|h| match named_map(h, recv, "@@maxListeners") {
87 Some(v @ (Value::Float(_) | Value::Int(_))) => v,
88 _ => Value::Float(10.0),
89 })),
90 "on" | "addListener" | "prependListener" | "once" | "prependOnceListener" => {
91 let once = matches!(method, "once" | "prependOnceListener");
92 let prepend = method.starts_with("prepend");
93 let name = event_key(&args);
94 let f = args.get(1).cloned().unwrap_or(Value::Undef);
95 if name != "newListener" && !listeners(recv, "newListener").is_empty() {
99 let nv = with_host(|h| h.new_str(name.clone()));
100 emit(recv, "newListener", &[nv, f.clone()])?;
101 }
102 add(
103 recv,
104 if once { "@@once" } else { "@@on" },
105 &name,
106 f,
107 prepend,
108 );
109 Ok(recv.clone())
110 }
111 "emit" => emit(
112 recv,
113 &event_key(&args),
114 &args.get(1..).map(|s| s.to_vec()).unwrap_or_default(),
115 ),
116 "removeListener" | "off" => {
117 let name = event_key(&args);
118 let f = args.get(1).cloned();
119 let had = f
120 .as_ref()
121 .is_some_and(|f| listeners(recv, &name).iter().any(|l| l == f));
122 remove(recv, &name, f.clone());
123 if had && name != "removeListener" && !listeners(recv, "removeListener").is_empty() {
126 let nv = with_host(|h| h.new_str(name.clone()));
127 emit(recv, "removeListener", &[nv, f.unwrap_or(Value::Undef)])?;
128 }
129 Ok(recv.clone())
130 }
131 "removeAllListeners" => {
132 let name = if args.is_empty() {
133 None
134 } else {
135 Some(event_key(&args))
136 };
137 remove_all(recv, name.as_deref());
138 Ok(recv.clone())
139 }
140 "listenerCount" => Ok(Value::Float(listeners(recv, &event_key(&args)).len() as f64)),
141 "eventNames" => Ok(with_host(|h| {
146 let mut keys: Vec<String> = Vec::new();
147 if let Some(JsObj::Object(p)) = named_map(h, recv, "@@on").and_then(|v| h.get(&v)) {
148 keys.extend(p.keys().cloned());
149 }
150 let (syms, strs): (Vec<String>, Vec<String>) = keys
151 .into_iter()
152 .partition(|k| crate::host::is_symbol_key(k));
153 let mut names: Vec<Value> = strs.into_iter().map(|k| h.new_str(k)).collect();
154 names.extend(
155 syms.iter()
156 .filter_map(|k| h.symbol_of_key(k))
157 .collect::<Vec<Value>>(),
158 );
159 h.new_array(names)
160 })),
161 _ => Err(crate::host::type_error(&format!(
162 "emitter.{method} is not a function"
163 ))),
164 }
165}
166
167fn named_map(h: &crate::host::JsHost, recv: &Value, which: &str) -> Option<Value> {
171 match h.get(recv) {
172 Some(JsObj::Object(p)) => p.get(which).cloned(),
173 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(recv, which),
174 _ => None,
175 }
176}
177
178fn set_named_map(h: &mut crate::host::JsHost, recv: &Value, which: &str, val: Value) {
180 match h.get(recv) {
181 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.set_fn_prop(recv, which, val),
182 _ => {
183 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
184 p.insert(which.to_string(), val);
185 }
186 }
187 }
188}
189
190fn add(recv: &Value, which: &str, name: &str, f: Value, prepend: bool) {
199 if which == "@@once" {
200 add_to_list(recv, "@@once", name, f.clone(), false);
202 }
203 add_to_list(recv, "@@on", name, f, prepend);
204}
205
206fn add_to_list(recv: &Value, which: &str, name: &str, f: Value, prepend: bool) {
207 with_host(|h| {
208 let map = match named_map(h, recv, which) {
210 Some(m) => m,
211 None => {
212 let m = h.new_object(IndexMap::new());
213 set_named_map(h, recv, which, m.clone());
214 m
215 }
216 };
217 let arr = match h.get(&map) {
219 Some(JsObj::Object(p)) => p.get(name).cloned(),
220 _ => None,
221 };
222 let arr = match arr {
223 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
224 _ => {
225 let a = h.new_array(Vec::new());
226 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
227 p.insert(name.to_string(), a.clone());
228 }
229 a
230 }
231 };
232 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
233 if prepend {
236 items.insert(0, f);
237 } else {
238 items.push(f);
239 }
240 }
241 });
242}
243
244fn remove_first(recv: &Value, which: &str, name: &str, f: &Value) {
247 with_host(|h| {
248 let Some(map) = named_map(h, recv, which) else {
249 return;
250 };
251 let arr = match h.get(&map) {
252 Some(JsObj::Object(p)) => p.get(name).cloned(),
253 _ => None,
254 };
255 let mut emptied = false;
256 if let Some(JsObj::Array(items)) = arr.and_then(|a| h.get_mut(&a)) {
257 if let Some(i) = items.iter().position(|x| x == f) {
258 items.remove(i);
259 }
260 emptied = items.is_empty();
261 }
262 if emptied {
265 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
266 p.shift_remove(name);
267 }
268 }
269 });
270}
271
272fn listeners(recv: &Value, name: &str) -> Vec<Value> {
273 with_host(|h| {
274 let mut out = Vec::new();
277 if let Some(map) = named_map(h, recv, "@@on") {
278 if let Some(JsObj::Object(p)) = h.get(&map) {
279 if let Some(a) = p.get(name) {
280 if let Some(JsObj::Array(items)) = h.get(a) {
281 out.extend(items.iter().cloned());
282 }
283 }
284 }
285 }
286 out
287 })
288}
289
290fn emit(recv: &Value, name: &str, args: &[Value]) -> Result<Value, String> {
291 let to_call = listeners(recv, name);
292 if name == "error" && to_call.is_empty() {
296 let err = args.first().cloned().unwrap_or(Value::Undef);
297 if matches!(err, Value::Undef) {
298 return Err(crate::host::plain_coded_error(
299 "Error",
300 "ERR_UNHANDLED_ERROR",
301 "Unhandled error.",
302 ));
303 }
304 let msg = with_host(|h| {
305 h.exc = Some(err.clone());
306 crate::builtins::error_string(h, &err)
307 });
308 return Err(msg);
309 }
310 let expired = remove_all_of(recv, "@@once", Some(name));
315 for f in &expired {
316 remove_first(recv, "@@on", name, f);
317 }
318 let had = !to_call.is_empty();
319 for f in to_call {
320 invoke(&f, args.to_vec(), Some(recv.clone()))?;
321 }
322 resolve_waiters(recv, name, args);
324 Ok(Value::Bool(had))
325}
326
327fn add_waiter(recv: &Value, name: &str, promise: Value) {
337 with_host(|h| {
338 let map = match waiter_map(h, recv) {
339 Some(m) => m,
340 None => {
341 let m = h.new_object(IndexMap::new());
342 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
343 p.insert("@@waiters".into(), m.clone());
344 }
345 m
346 }
347 };
348 let existing = match h.get(&map) {
349 Some(JsObj::Object(p)) => p.get(name).cloned(),
350 _ => None,
351 };
352 let arr = match existing {
353 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
354 _ => {
355 let a = h.new_array(Vec::new());
356 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
357 p.insert(name.to_string(), a.clone());
358 }
359 a
360 }
361 };
362 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
363 items.push(promise);
364 }
365 });
366}
367
368fn waiter_map(h: &crate::host::JsHost, recv: &Value) -> Option<Value> {
369 match h.get(recv) {
370 Some(JsObj::Object(p)) => p.get("@@waiters").cloned(),
371 _ => None,
372 }
373}
374
375fn take_waiters(recv: &Value, name: &str) -> Vec<Value> {
377 with_host(|h| {
378 let Some(map) = waiter_map(h, recv) else {
379 return Vec::new();
380 };
381 let arr = match h.get_mut(&map) {
382 Some(JsObj::Object(p)) => p.shift_remove(name),
383 _ => None,
384 };
385 let Some(arr) = arr else { return Vec::new() };
386 match h.get(&arr) {
387 Some(JsObj::Array(items)) => items.clone(),
388 _ => Vec::new(),
389 }
390 })
391}
392
393fn take_waiters_except(recv: &Value, keep: &str) -> Vec<Value> {
395 with_host(|h| {
396 let Some(map) = waiter_map(h, recv) else {
397 return Vec::new();
398 };
399 let keys: Vec<String> = match h.get(&map) {
400 Some(JsObj::Object(p)) => p.keys().filter(|k| k.as_str() != keep).cloned().collect(),
401 _ => Vec::new(),
402 };
403 let mut out = Vec::new();
404 for k in keys {
405 let arr = match h.get_mut(&map) {
406 Some(JsObj::Object(p)) => p.shift_remove(&k),
407 _ => None,
408 };
409 if let Some(arr) = arr {
410 if let Some(JsObj::Array(items)) = h.get(&arr) {
411 out.extend(items.iter().cloned());
412 }
413 }
414 }
415 out
416 })
417}
418
419fn resolve_waiters(recv: &Value, name: &str, args: &[Value]) {
420 let waiting = take_waiters(recv, name);
421 if !waiting.is_empty() {
422 let arr = with_host(|h| h.new_array(args.to_vec()));
423 for p in &waiting {
424 if let Some(id) = with_host(|h| h.promise_id(p)) {
425 crate::host::resolve_promise_val(id, arr.clone());
426 }
427 }
428 }
429 if name == "error" {
430 let err = args.first().cloned().unwrap_or(Value::Undef);
431 for p in take_waiters_except(recv, "error") {
432 if let Some(id) = with_host(|h| h.promise_id(&p)) {
433 crate::host::reject_promise_val(id, err.clone());
434 }
435 }
436 }
437}
438
439pub const STATIC_METHODS: &[&str] = &[
445 "once",
446 "listenerCount",
447 "getEventListeners",
448 "getMaxListeners",
449 "setMaxListeners",
450 "addAbortListener",
451 "init",
452];
453
454pub fn static_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
457 let emitter = args.first().cloned().unwrap_or(Value::Undef);
458 Some(match method {
459 "once" => Ok(once_static(emitter, &arg_str(args, 1))),
460 "listenerCount" => Ok(Value::Float(
461 listeners(&emitter, &arg_str(args, 1)).len() as f64
462 )),
463 "getEventListeners" => {
468 let found = listeners(&emitter, &arg_str(args, 1));
469 Ok(with_host(|h| h.new_array(found)))
470 }
471 "getMaxListeners" => Ok(Value::Float(10.0)),
473 "setMaxListeners" => Ok(Value::Undef),
474 "addAbortListener" => Ok(add_abort_listener(args)),
475 "init" => Ok(init_emitter(emitter)),
476 _ => return None,
477 })
478}
479
480fn once_static(emitter: Value, name: &str) -> Value {
483 let p = with_host(|h| h.new_promise());
484 add_waiter(&emitter, name, p.clone());
485 p
486}
487
488fn init_emitter(emitter: Value) -> Value {
491 with_host(|h| {
492 let has = matches!(h.get(&emitter), Some(JsObj::Object(p)) if p.contains_key("@@on"));
493 if !has {
494 let on = h.new_object(IndexMap::new());
495 let once = h.new_object(IndexMap::new());
496 let native = h.new_str("EventEmitter");
497 if let Some(JsObj::Object(p)) = h.get_mut(&emitter) {
498 p.entry("@@native".to_string()).or_insert(native);
499 p.insert("@@on".to_string(), on);
500 p.insert("@@once".to_string(), once);
501 }
502 }
503 });
504 emitter
505}
506
507fn add_abort_listener(args: &[Value]) -> Value {
512 let signal = args.first().cloned().unwrap_or(Value::Undef);
513 let listener = args.get(1).cloned().unwrap_or(Value::Undef);
514 let name = with_host(|h| h.new_str("abort"));
515 let _ = call_method(&signal, "once", vec![name, listener]);
516 with_host(|h| h.new_object(IndexMap::new()))
517}
518
519fn remove(recv: &Value, name: &str, f: Option<Value>) {
520 let Some(f) = f else { return };
521 with_host(|h| {
522 for which in ["@@on", "@@once"] {
523 if let Some(map) = named_map(h, recv, which) {
524 let arr = match h.get(&map) {
525 Some(JsObj::Object(p)) => p.get(name).cloned(),
526 _ => None,
527 };
528 if let Some(a) = arr {
529 let now_empty = if let Some(JsObj::Array(items)) = h.get_mut(&a) {
530 if let Some(pos) = items.iter().position(|x| x == &f) {
531 items.remove(pos);
532 }
533 items.is_empty()
534 } else {
535 false
536 };
537 if now_empty {
540 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
541 p.shift_remove(name);
542 }
543 }
544 }
545 }
546 }
547 });
548}
549
550fn remove_all(recv: &Value, name: Option<&str>) {
551 remove_all_of(recv, "@@on", name);
552 remove_all_of(recv, "@@once", name);
553}
554
555fn remove_all_of(recv: &Value, which: &str, name: Option<&str>) -> Vec<Value> {
558 with_host(|h| {
559 let mut dropped = Vec::new();
560 if let Some(map) = named_map(h, recv, which) {
561 let arrays: Vec<Value> = match h.get(&map) {
562 Some(JsObj::Object(p)) => match name {
563 Some(n) => p.get(n).cloned().into_iter().collect(),
564 None => p.values().cloned().collect(),
565 },
566 _ => Vec::new(),
567 };
568 for a in arrays {
569 if let Some(JsObj::Array(items)) = h.get(&a) {
570 dropped.extend(items.iter().cloned());
571 }
572 }
573 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
574 match name {
575 Some(n) => {
576 p.shift_remove(n);
577 }
578 None => p.clear(),
579 }
580 }
581 }
582 dropped
583 })
584}