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" => {
51 let items = listeners(recv, &arg_str(&args, 0));
52 Ok(with_host(|h| h.new_array(items)))
53 }
54 "setMaxListeners" => Ok(recv.clone()),
56 "getMaxListeners" => Ok(Value::Float(10.0)),
57 "on" | "addListener" | "prependListener" => {
58 add(
59 recv,
60 "@@on",
61 &arg_str(&args, 0),
62 args.get(1).cloned().unwrap_or(Value::Undef),
63 );
64 Ok(recv.clone())
65 }
66 "once" | "prependOnceListener" => {
67 add(
68 recv,
69 "@@once",
70 &arg_str(&args, 0),
71 args.get(1).cloned().unwrap_or(Value::Undef),
72 );
73 Ok(recv.clone())
74 }
75 "emit" => emit(
76 recv,
77 &arg_str(&args, 0),
78 &args.get(1..).map(|s| s.to_vec()).unwrap_or_default(),
79 ),
80 "removeListener" | "off" => {
81 remove(recv, &arg_str(&args, 0), args.get(1).cloned());
82 Ok(recv.clone())
83 }
84 "removeAllListeners" => {
85 let name = if args.is_empty() {
86 None
87 } else {
88 Some(arg_str(&args, 0))
89 };
90 remove_all(recv, name.as_deref());
91 Ok(recv.clone())
92 }
93 "listenerCount" => Ok(Value::Float(
94 listeners(recv, &arg_str(&args, 0)).len() as f64
95 )),
96 "eventNames" => Ok(with_host(|h| {
97 let mut keys: Vec<String> = Vec::new();
98 for map in ["@@on", "@@once"] {
99 if let Some(JsObj::Object(p)) = named_map(h, recv, map).and_then(|v| h.get(&v)) {
100 keys.extend(p.keys().cloned());
101 }
102 }
103 let names: Vec<Value> = keys.into_iter().map(|k| h.new_str(k)).collect();
104 h.new_array(names)
105 })),
106 _ => Err(crate::host::type_error(&format!(
107 "emitter.{method} is not a function"
108 ))),
109 }
110}
111
112fn named_map(h: &crate::host::JsHost, recv: &Value, which: &str) -> Option<Value> {
116 match h.get(recv) {
117 Some(JsObj::Object(p)) => p.get(which).cloned(),
118 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(recv, which),
119 _ => None,
120 }
121}
122
123fn set_named_map(h: &mut crate::host::JsHost, recv: &Value, which: &str, val: Value) {
125 match h.get(recv) {
126 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.set_fn_prop(recv, which, val),
127 _ => {
128 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
129 p.insert(which.to_string(), val);
130 }
131 }
132 }
133}
134
135fn add(recv: &Value, which: &str, name: &str, f: Value) {
136 with_host(|h| {
137 let map = match named_map(h, recv, which) {
139 Some(m) => m,
140 None => {
141 let m = h.new_object(IndexMap::new());
142 set_named_map(h, recv, which, m.clone());
143 m
144 }
145 };
146 let arr = match h.get(&map) {
148 Some(JsObj::Object(p)) => p.get(name).cloned(),
149 _ => None,
150 };
151 let arr = match arr {
152 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
153 _ => {
154 let a = h.new_array(Vec::new());
155 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
156 p.insert(name.to_string(), a.clone());
157 }
158 a
159 }
160 };
161 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
162 items.push(f);
163 }
164 });
165}
166
167fn listeners(recv: &Value, name: &str) -> Vec<Value> {
168 with_host(|h| {
169 let mut out = Vec::new();
170 for which in ["@@on", "@@once"] {
171 if let Some(map) = named_map(h, recv, which) {
172 if let Some(JsObj::Object(p)) = h.get(&map) {
173 if let Some(a) = p.get(name) {
174 if let Some(JsObj::Array(items)) = h.get(a) {
175 out.extend(items.iter().cloned());
176 }
177 }
178 }
179 }
180 }
181 out
182 })
183}
184
185fn emit(recv: &Value, name: &str, args: &[Value]) -> Result<Value, String> {
186 let to_call = listeners(recv, name);
187 remove_all_of(recv, "@@once", Some(name));
189 let had = !to_call.is_empty();
190 for f in to_call {
191 invoke(&f, args.to_vec(), Some(recv.clone()))?;
192 }
193 resolve_waiters(recv, name, args);
195 Ok(Value::Bool(had))
196}
197
198fn add_waiter(recv: &Value, name: &str, promise: Value) {
208 with_host(|h| {
209 let map = match waiter_map(h, recv) {
210 Some(m) => m,
211 None => {
212 let m = h.new_object(IndexMap::new());
213 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
214 p.insert("@@waiters".into(), m.clone());
215 }
216 m
217 }
218 };
219 let existing = match h.get(&map) {
220 Some(JsObj::Object(p)) => p.get(name).cloned(),
221 _ => None,
222 };
223 let arr = match existing {
224 Some(a) if matches!(h.get(&a), Some(JsObj::Array(_))) => a,
225 _ => {
226 let a = h.new_array(Vec::new());
227 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
228 p.insert(name.to_string(), a.clone());
229 }
230 a
231 }
232 };
233 if let Some(JsObj::Array(items)) = h.get_mut(&arr) {
234 items.push(promise);
235 }
236 });
237}
238
239fn waiter_map(h: &crate::host::JsHost, recv: &Value) -> Option<Value> {
240 match h.get(recv) {
241 Some(JsObj::Object(p)) => p.get("@@waiters").cloned(),
242 _ => None,
243 }
244}
245
246fn take_waiters(recv: &Value, name: &str) -> Vec<Value> {
248 with_host(|h| {
249 let Some(map) = waiter_map(h, recv) else {
250 return Vec::new();
251 };
252 let arr = match h.get_mut(&map) {
253 Some(JsObj::Object(p)) => p.shift_remove(name),
254 _ => None,
255 };
256 let Some(arr) = arr else { return Vec::new() };
257 match h.get(&arr) {
258 Some(JsObj::Array(items)) => items.clone(),
259 _ => Vec::new(),
260 }
261 })
262}
263
264fn take_waiters_except(recv: &Value, keep: &str) -> Vec<Value> {
266 with_host(|h| {
267 let Some(map) = waiter_map(h, recv) else {
268 return Vec::new();
269 };
270 let keys: Vec<String> = match h.get(&map) {
271 Some(JsObj::Object(p)) => p.keys().filter(|k| k.as_str() != keep).cloned().collect(),
272 _ => Vec::new(),
273 };
274 let mut out = Vec::new();
275 for k in keys {
276 let arr = match h.get_mut(&map) {
277 Some(JsObj::Object(p)) => p.shift_remove(&k),
278 _ => None,
279 };
280 if let Some(arr) = arr {
281 if let Some(JsObj::Array(items)) = h.get(&arr) {
282 out.extend(items.iter().cloned());
283 }
284 }
285 }
286 out
287 })
288}
289
290fn resolve_waiters(recv: &Value, name: &str, args: &[Value]) {
291 let waiting = take_waiters(recv, name);
292 if !waiting.is_empty() {
293 let arr = with_host(|h| h.new_array(args.to_vec()));
294 for p in &waiting {
295 if let Some(id) = with_host(|h| h.promise_id(p)) {
296 crate::host::resolve_promise_val(id, arr.clone());
297 }
298 }
299 }
300 if name == "error" {
301 let err = args.first().cloned().unwrap_or(Value::Undef);
302 for p in take_waiters_except(recv, "error") {
303 if let Some(id) = with_host(|h| h.promise_id(&p)) {
304 crate::host::reject_promise_val(id, err.clone());
305 }
306 }
307 }
308}
309
310pub const STATIC_METHODS: &[&str] = &[
316 "once",
317 "listenerCount",
318 "getEventListeners",
319 "getMaxListeners",
320 "setMaxListeners",
321 "addAbortListener",
322 "init",
323];
324
325pub fn static_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
328 let emitter = args.first().cloned().unwrap_or(Value::Undef);
329 Some(match method {
330 "once" => Ok(once_static(emitter, &arg_str(args, 1))),
331 "listenerCount" => Ok(Value::Float(
332 listeners(&emitter, &arg_str(args, 1)).len() as f64
333 )),
334 "getEventListeners" => {
339 let found = listeners(&emitter, &arg_str(args, 1));
340 Ok(with_host(|h| h.new_array(found)))
341 }
342 "getMaxListeners" => Ok(Value::Float(10.0)),
344 "setMaxListeners" => Ok(Value::Undef),
345 "addAbortListener" => Ok(add_abort_listener(args)),
346 "init" => Ok(init_emitter(emitter)),
347 _ => return None,
348 })
349}
350
351fn once_static(emitter: Value, name: &str) -> Value {
354 let p = with_host(|h| h.new_promise());
355 add_waiter(&emitter, name, p.clone());
356 p
357}
358
359fn init_emitter(emitter: Value) -> Value {
362 with_host(|h| {
363 let has = matches!(h.get(&emitter), Some(JsObj::Object(p)) if p.contains_key("@@on"));
364 if !has {
365 let on = h.new_object(IndexMap::new());
366 let once = h.new_object(IndexMap::new());
367 let native = h.new_str("EventEmitter");
368 if let Some(JsObj::Object(p)) = h.get_mut(&emitter) {
369 p.entry("@@native".to_string()).or_insert(native);
370 p.insert("@@on".to_string(), on);
371 p.insert("@@once".to_string(), once);
372 }
373 }
374 });
375 emitter
376}
377
378fn add_abort_listener(args: &[Value]) -> Value {
383 let signal = args.first().cloned().unwrap_or(Value::Undef);
384 let listener = args.get(1).cloned().unwrap_or(Value::Undef);
385 let name = with_host(|h| h.new_str("abort"));
386 let _ = call_method(&signal, "once", vec![name, listener]);
387 with_host(|h| h.new_object(IndexMap::new()))
388}
389
390fn remove(recv: &Value, name: &str, f: Option<Value>) {
391 let Some(f) = f else { return };
392 with_host(|h| {
393 for which in ["@@on", "@@once"] {
394 if let Some(map) = named_map(h, recv, which) {
395 let arr = match h.get(&map) {
396 Some(JsObj::Object(p)) => p.get(name).cloned(),
397 _ => None,
398 };
399 if let Some(a) = arr {
400 let now_empty = if let Some(JsObj::Array(items)) = h.get_mut(&a) {
401 if let Some(pos) = items.iter().position(|x| x == &f) {
402 items.remove(pos);
403 }
404 items.is_empty()
405 } else {
406 false
407 };
408 if now_empty {
411 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
412 p.shift_remove(name);
413 }
414 }
415 }
416 }
417 }
418 });
419}
420
421fn remove_all(recv: &Value, name: Option<&str>) {
422 remove_all_of(recv, "@@on", name);
423 remove_all_of(recv, "@@once", name);
424}
425
426fn remove_all_of(recv: &Value, which: &str, name: Option<&str>) {
427 with_host(|h| {
428 if let Some(map) = named_map(h, recv, which) {
429 if let Some(JsObj::Object(p)) = h.get_mut(&map) {
430 match name {
431 Some(n) => {
432 p.shift_remove(n);
433 }
434 None => p.clear(),
435 }
436 }
437 }
438 });
439}