1use crate::host::{with_host, JsObj};
19use fusevm::Value;
20
21pub mod assert;
22pub mod assert_diff;
23pub mod async_hooks;
24pub mod buffer;
25pub mod child_process;
26pub mod cluster;
27pub mod console;
28pub mod constants;
29pub mod crypto;
30pub mod date;
31pub mod dgram;
32pub mod diagnostics_channel;
33pub mod dns;
34pub mod domain;
35pub mod events;
36pub mod fetch;
37pub mod fs;
38pub mod fs_promises;
39pub mod http;
40pub mod http2;
41pub mod https;
42pub mod iterator;
43pub mod net;
44pub mod node_module;
45pub mod os;
46pub mod path;
47pub mod perf_hooks;
48pub mod process;
49pub mod punycode;
50pub mod querystring;
51pub mod readline;
52pub mod repl;
53pub mod stream;
54pub mod stream_consumers;
55pub mod stream_promises;
56pub mod stream_web;
57pub mod string_decoder;
58pub mod timers;
59pub mod tls;
60pub mod trace_events;
61pub mod tty;
62pub mod typedarray;
63pub mod url;
64pub mod url_legacy;
65pub mod util;
66pub mod util_types;
67pub mod v8;
68pub mod vm;
69pub mod worker_threads;
70pub mod zlib;
71
72pub const UNIMPLEMENTED_MODULES: &[&str] = &["inspector", "wasi"];
79
80pub fn is_unimplemented(ns: &str) -> bool {
82 UNIMPLEMENTED_MODULES.contains(&ns)
83}
84
85pub fn data_module(spec: &str) -> Option<Value> {
92 match spec.strip_prefix("node:").unwrap_or(spec) {
93 "constants" => Some(constants::object(&constants::flat())),
94 _ => None,
95 }
96}
97
98pub fn is_core(spec: &str) -> bool {
101 resolve(spec).is_some() || matches!(spec.strip_prefix("node:").unwrap_or(spec), "constants")
102}
103
104pub fn resolve(spec: &str) -> Option<&'static str> {
105 match spec.strip_prefix("node:").unwrap_or(spec) {
106 "fs" => Some("fs"),
107 "path" => Some("path"),
108 "os" => Some("os"),
109 "util" => Some("util"),
110 "assert" => Some("assert"),
111 "crypto" => Some("crypto"),
112 "buffer" => Some("buffer"),
113 "url" => Some("url"),
114 "process" => Some("process"),
115 "net" => Some("net"),
116 "http" => Some("http"),
117 "stream" => Some("Stream"),
123 "tty" => Some("tty"),
124 "events" => Some("EventEmitter"),
127 "string_decoder" => Some("string_decoder"),
128 "zlib" => Some("zlib"),
129 "querystring" => Some("querystring"),
130 "console" => Some("console"),
131 "path/posix" => Some("path"),
135 "path/win32" => Some("path/win32"),
136 "sys" => Some("util"),
138 "assert/strict" => Some("assertStrict"),
143 "child_process" => Some("child_process"),
144 "dns" => Some("dns"),
145 "punycode" => Some("punycode"),
146 "timers" => Some("timers"),
147 "timers/promises" => Some("timers/promises"),
148 "perf_hooks" => Some("perf_hooks"),
149 "async_hooks" => Some("async_hooks"),
150 "util/types" => Some("util/types"),
151 "diagnostics_channel" => Some("diagnostics_channel"),
152 "v8" => Some("v8"),
153 "readline" => Some("readline"),
154 "readline/promises" => Some("readline/promises"),
155 "vm" => Some("vm"),
156 "fs/promises" => Some("fs/promises"),
157 "dgram" => Some("dgram"),
158 "dns/promises" => Some("dns/promises"),
159 "worker_threads" => Some("worker_threads"),
160 "tls" => Some("tls"),
161 "https" => Some("https"),
162 "repl" => Some("repl"),
163 "cluster" => Some("cluster"),
164 "domain" => Some("domain"),
165 "http2" => Some("http2"),
166 "trace_events" => Some("trace_events"),
167 "module" => Some("module"),
168 "stream/consumers" => Some("stream/consumers"),
169 "stream/promises" => Some("stream/promises"),
170 "stream/web" => Some("stream/web"),
171 other => UNIMPLEMENTED_MODULES.iter().copied().find(|&m| m == other),
172 }
173}
174
175pub fn is_method(qualified: &str) -> bool {
178 let Some((ns, m)) = qualified.split_once('.') else {
179 return qualified == "assert";
180 };
181 is_unimplemented(ns) || namespace_methods(ns).contains(&m) || namespace_ctors(ns).contains(&m)
185}
186
187pub fn namespace_methods(ns: &str) -> &'static [&'static str] {
191 match ns {
192 "fs" => fs::METHODS,
193 "path" | "path/win32" => path::METHODS,
194 "os" => os::METHODS,
195 "util" => util::METHODS,
196 "assert" | "assertStrict" => assert::METHODS,
197 "crypto" => crypto::METHODS,
198 "webcrypto" => crypto::WEBCRYPTO_METHODS,
199 "SubtleCrypto" => crypto::SUBTLE_METHODS,
200 "Buffer" => buffer::STATIC_METHODS,
201 "buffer" => buffer::MODULE_METHODS,
202 "Date" => date::STATIC_METHODS,
203 "Response" => fetch::RESPONSE_STATICS,
204 "AbortSignal" => fetch::ABORT_SIGNAL_STATICS,
205 "Iterator" => iterator::STATIC_METHODS,
206 n if typedarray::is_ctor(n) => typedarray::static_methods(n),
207 "URL" => url::STATIC_METHODS,
208 "url" => url::MODULE_METHODS,
209 "net" => net::MODULE_METHODS,
210 "http" => http::MODULE_METHODS,
211 "stream" | "Stream" => stream::METHODS,
212 n if stream::is_class(n) => stream::STATIC_METHODS,
213 "worker_threads" => worker_threads::METHODS,
214 "zlib" => zlib::MODULE_METHODS,
215 "querystring" => querystring::METHODS,
216 "tty" => tty::METHODS,
217 "process" => process::METHODS,
218 "EventEmitter" => events::STATIC_METHODS,
219 "console" => console::METHODS,
220 "child_process" => child_process::METHODS,
221 "dns" => dns::METHODS,
222 "dns/promises" => dns::PROMISES_METHODS,
223 "punycode" => punycode::METHODS,
224 "timers" => timers::METHODS,
225 "timers/promises" => timers::PROMISES_METHODS,
226 "perf_hooks" | "performance" => perf_hooks::METHODS,
227 "async_hooks" => async_hooks::METHODS,
228 "AsyncResource" => async_hooks::RESOURCE_STATIC_METHODS,
229 "util/types" => util_types::METHODS,
230 "diagnostics_channel" => diagnostics_channel::METHODS,
231 "v8" => v8::METHODS,
232 "readline" => readline::METHODS,
233 "readline/promises" => readline::PROMISES_METHODS,
234 "vm" => vm::METHODS,
235 "fs/promises" => fs_promises::METHODS,
236 "dgram" => dgram::MODULE_METHODS,
237 "tls" => tls::MODULE_METHODS,
238 "https" => https::MODULE_METHODS,
239 "repl" => repl::METHODS,
240 "cluster" => cluster::METHODS,
241 "domain" => domain::METHODS,
242 "http2" => http2::METHODS,
243 "trace_events" => trace_events::METHODS,
244 "module" => node_module::METHODS,
245 "Module" => node_module::MODULE_STATIC_METHODS,
246 "stream/consumers" => stream_consumers::METHODS,
247 "stream/promises" => stream_promises::METHODS,
248 _ => &[],
249 }
250}
251
252pub fn namespace_ctors(ns: &str) -> &'static [&'static str] {
256 match ns {
257 "buffer" => &["Buffer", "Blob", "File"],
258 "stream" | "Stream" => stream::CLASSES,
259 "url" => &["URL", "URLSearchParams"],
260 "EventEmitter" => &["EventEmitter"],
261 "async_hooks" => &["AsyncLocalStorage", "AsyncResource"],
262 "string_decoder" => &["StringDecoder"],
263 "assert" => &["AssertionError"],
264 "console" => &["Console"],
265 "vm" => &["Script"],
266 "fs" => &["promises"],
267 "stream/web" => stream_web::CLASSES,
275 _ => &[],
276 }
277}
278
279pub fn namespace_keys(ns: &str) -> Vec<String> {
285 if ns == crate::builtins::REQUIRE_CACHE {
287 return crate::module::cache_keys();
288 }
289 let mut out: Vec<String> = namespace_ctors(ns).iter().map(|s| s.to_string()).collect();
290 for m in namespace_methods(ns) {
291 if !out.iter().any(|k| k == m) {
292 out.push((*m).to_string());
293 }
294 }
295 out
296}
297
298pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, String>> {
302 if name == "assert" {
303 return Some(assert::assert_ok(args));
304 }
305 let (ns, m) = name.split_once('.')?;
306 Some(match ns {
307 "fs" => fs::call(m, args)?,
308 "path" => path::call(path::Flavor::Posix, m, args)?,
309 "path/win32" => path::call(path::Flavor::Win32, m, args)?,
310 "os" => os::call(m, args)?,
311 "util" => util::call(m, args)?,
312 "assert" => assert::call(m, args)?,
313 "assertStrict" => assert::strict_call(m, args)?,
314 "crypto" => crypto::call(m, args)?,
315 "webcrypto" => crypto::call(m, args)?,
318 "SubtleCrypto" => crypto::subtle_call(m, args)?,
319 "Buffer" => buffer::static_call(m, args)?,
320 "buffer" if m == "Buffer" => Ok(with_host(|h| h.alloc(JsObj::Builtin("Buffer".into())))),
321 "buffer" => buffer::module_call(m, args)?,
322 "Date" => date::static_call(m, args)?,
323 "Response" | "AbortSignal" => fetch::static_call(ns, m, args)?,
324 "Iterator" => iterator::static_call(m, args)?,
325 n if typedarray::is_ctor(n) => typedarray::static_call(n, m, args)?,
326 "URL" => url::static_call(m, args)?,
327 "url" if m == "URL" => Ok(with_host(|h| h.alloc(JsObj::Builtin("URL".into())))),
328 "url" => url::call(m, args)?,
329 "net" => net::call(m, args)?,
330 "http" => http::call(m, args)?,
331 "stream" | "Stream" => stream::call(m, args)?,
332 "worker_threads" => worker_threads::call(m, args)?,
333 "zlib" => zlib::call(m, args)?,
334 "querystring" => querystring::call(m, args)?,
335 "tty" => tty::call(m, args)?,
336 "process" => process::call(m, args)?,
337 "EventEmitter" if m == "EventEmitter" => Ok(with_host(|h| {
338 h.alloc(JsObj::Builtin("EventEmitter".into()))
339 })),
340 "EventEmitter" => events::static_call(m, args)?,
341 n if stream::is_class(n) => stream::static_call(n, m, args)?,
342 "console" => console::call(m, args)?,
343 "child_process" => child_process::call(m, args)?,
344 "dns" => dns::call(m, args)?,
345 "punycode" => punycode::call(m, args)?,
346 "timers" => timers::call(m, args)?,
347 "timers/promises" => timers::promises_call(m, args)?,
348 "perf_hooks" | "performance" => perf_hooks::call(m, args)?,
349 "async_hooks" => async_hooks::call(m, args)?,
350 "AsyncResource" => async_hooks::static_call(m, args)?,
351 "util/types" => util_types::call(m, args)?,
352 "diagnostics_channel" => diagnostics_channel::call(m, args)?,
353 "v8" => v8::call(m, args)?,
354 "readline" => readline::call(m, args)?,
355 "readline/promises" => readline::promises_call(m, args)?,
356 "vm" => vm::call(m, args)?,
357 "fs/promises" => fs_promises::call(m, args)?,
358 "dgram" => dgram::call(m, args)?,
359 "dns/promises" => match m {
362 "getServers" | "setServers" | "getDefaultResultOrder" | "setDefaultResultOrder" => {
363 dns::call(m, args)?
364 }
365 _ => {
366 let mut pm = String::from("promise");
367 let mut cs = m.chars();
368 if let Some(c) = cs.next() {
369 pm.extend(c.to_uppercase());
370 pm.push_str(cs.as_str());
371 }
372 dns::call(&pm, args)?
373 }
374 },
375 "tls" => tls::call(m, args)?,
376 "https" => https::call(m, args)?,
377 "repl" => repl::call(m, args)?,
378 "cluster" => cluster::call(m, args)?,
379 "domain" => domain::call(m, args)?,
380 "http2" => http2::call(m, args)?,
381 "trace_events" => trace_events::call(m, args)?,
382 "module" => node_module::call(m, args)?,
383 "Module" => node_module::static_call(m, args)?,
384 "stream/consumers" => stream_consumers::call(m, args)?,
385 "stream/promises" => stream_promises::call(m, args)?,
386 _ if is_unimplemented(ns) => Err(format!("Error: {ns}.{m} is not implemented in node-js")),
387 _ => return None,
388 })
389}
390
391pub fn constant(ns: &str, name: &str) -> Option<Value> {
394 match ns {
395 "path" | "path/win32" if name == "posix" => {
398 Some(with_host(|h| h.alloc(JsObj::Builtin("path".into()))))
399 }
400 "path" | "path/win32" if name == "win32" => {
401 Some(with_host(|h| h.alloc(JsObj::Builtin("path/win32".into()))))
402 }
403 "path" => path::constant(path::Flavor::Posix, name),
404 "path/win32" => path::constant(path::Flavor::Win32, name),
405 "os" => os::constant(name),
406 "fs" | "fs/promises" if name == "constants" => Some(constants::object(&constants::fs())),
411 "crypto" if name == "constants" => Some(constants::object(&constants::crypto())),
412 "crypto" if name == "webcrypto" => {
416 Some(with_host(|h| h.alloc(JsObj::Builtin("webcrypto".into()))))
417 }
418 "webcrypto" if name == "subtle" => Some(with_host(|h| {
419 h.alloc(JsObj::Builtin("SubtleCrypto".into()))
420 })),
421 "EventEmitter" | "events" if name == "defaultMaxListeners" => Some(Value::Float(10.0)),
425 "Buffer" if name == "poolSize" => Some(Value::Float(65536.0)),
431 n if typedarray::is_ctor(n) && name == "BYTES_PER_ELEMENT" => {
436 typedarray::ELEMENT_KINDS
438 .contains(&n)
439 .then(|| Value::Float(typedarray::bytes_per_element(n) as f64))
440 }
441 "buffer" if name == "Buffer" => {
442 Some(with_host(|h| h.alloc(JsObj::Builtin("Buffer".into()))))
443 }
444 "buffer" if matches!(name, "Blob" | "File") => {
445 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
446 }
447 "url" if name == "URL" => Some(with_host(|h| h.alloc(JsObj::Builtin("URL".into())))),
448 "net" => net::constant(name),
449 "tty" => tty::constant(name),
450 "repl" => repl::constant(name),
451 "readline" if name == "promises" => Some(with_host(|h| {
454 h.alloc(JsObj::Builtin("readline/promises".into()))
455 })),
456 "readline" | "readline/promises" => readline::constant(name),
457 "diagnostics_channel" => diagnostics_channel::constant(name),
458 "v8" => v8::constant(name),
459 "console" if name == "Console" => {
460 Some(with_host(|h| h.alloc(JsObj::Builtin("Console".into()))))
461 }
462 "assert" if name == "AssertionError" => Some(with_host(|h| {
463 h.alloc(JsObj::Builtin("AssertionError".into()))
464 })),
465 "assert" if name == "strict" => Some(with_host(|h| {
466 h.alloc(JsObj::Builtin("assertStrict".into()))
467 })),
468 n if stream::is_class(n) && name == "prototype" => with_host(|h| h.ensure_ctor_proto(n)),
473 "stream" | "Stream" => stream::constant(name),
474 "http" => http::constant(name),
475 "string_decoder" if name == "StringDecoder" => Some(with_host(|h| {
476 h.alloc(JsObj::Builtin("StringDecoder".into()))
477 })),
478 "process" => process::constant(name),
479 "EventEmitter" if name == "EventEmitter" => Some(with_host(|h| {
480 h.alloc(JsObj::Builtin("EventEmitter".into()))
481 })),
482 "perf_hooks" | "performance" => perf_hooks::constant(name),
483 "dns" => dns::constant(name),
484 "punycode" => punycode::constant(name),
485 "async_hooks" if matches!(name, "AsyncLocalStorage" | "AsyncResource") => {
486 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
487 }
488 "vm" if name == "Script" => Some(with_host(|h| h.alloc(JsObj::Builtin("Script".into())))),
489 "url" if name == "URLSearchParams" => Some(with_host(|h| {
490 h.alloc(JsObj::Builtin("URLSearchParams".into()))
491 })),
492 "fs" if name == "promises" => {
493 Some(with_host(|h| h.alloc(JsObj::Builtin("fs/promises".into()))))
494 }
495 "worker_threads" => worker_threads::constant(name),
496 "https" => https::constant(name),
497 "cluster" => cluster::constant(name),
498 "domain" => domain::constant(name),
499 "http2" => http2::constant(name),
500 "module" => node_module::constant(name),
501 "Module" => node_module::static_constant(name),
502 "stream/web" => stream_web::constant(name),
503 "util" => util::constant(name),
505 "crypto"
508 if matches!(
509 name,
510 "Sign"
511 | "Verify"
512 | "KeyObject"
513 | "DiffieHellman"
514 | "ECDH"
515 | "X509Certificate"
516 | "Hash"
517 | "Hmac"
518 | "Cipheriv"
519 | "Decipheriv"
520 ) =>
521 {
522 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
523 }
524 _ => None,
525 }
526}
527
528pub fn construct(name: &str, args: &[Value]) -> Option<Result<Value, String>> {
532 match name {
533 "URL" => Some(url::construct(args)),
534 "EventEmitter" => Some(Ok(events::new_emitter())),
535 "Buffer" => {
544 let numeric = matches!(args.first(), Some(Value::Int(_)) | Some(Value::Float(_)))
545 && args.len() == 1;
546 let m = if numeric { "alloc" } else { "from" };
547 Some(buffer::static_call(m, args).unwrap_or(Ok(Value::Undef)))
548 }
549 "Date" => Some(date::construct(args)),
550 "StringDecoder" => Some(string_decoder::construct(args)),
551 "WeakRef" => Some(typedarray::construct_weakref(args)),
552 "FinalizationRegistry" => Some(typedarray::construct_finalization_registry(args)),
553 n if fetch::is_class(n) => fetch::construct(n, args),
554 "TextEncoder" => Some(typedarray::construct_text_encoder()),
555 "TextDecoder" => Some(typedarray::construct_text_decoder(args)),
556 "DataView" => Some(typedarray::construct_dataview(args)),
557 n if typedarray::is_ctor(n) => Some(typedarray::construct(n, args)),
558 n if stream::is_class(n) => Some(Ok(stream::construct(n, args))),
559 "AsyncLocalStorage" | "AsyncResource" => async_hooks::construct(name, args),
560 "Script" => Some(vm::construct(args)),
561 "URLSearchParams" => Some(url::construct_search_params(args)),
562 "Worker" => Some(worker_threads::construct_worker(args)),
563 "Domain" => Some(domain::construct(args)),
564 "Tracing" => Some(trace_events::construct(args)),
565 "Blob" => Some(buffer::construct_blob(args)),
566 "File" => Some(buffer::construct_file(args)),
567 "AssertionError" => Some(Ok(assert::construct_assertion_error(args))),
568 "X509Certificate" => Some(crypto::construct_x509(args)),
569 "MIMEType" => Some(util::construct_mime_type(args)),
570 "MIMEParams" => Some(util::construct_mime_params(args)),
571 "Resolver" => Some(Ok(dns::construct_resolver(args))),
572 "ReadStream" | "WriteStream" => Some(Ok(tty::construct(name, args))),
573 "MessageChannel" => Some(worker_threads::construct_message_channel(args)),
574 "BroadcastChannel" => Some(worker_threads::construct_broadcast_channel(args)),
575 "PerformanceObserver" => Some(perf_hooks::construct(name, args)),
576 "REPLServer" | "Recoverable" => Some(repl::construct(name, args)),
577 "Interface" => Some(readline::construct(args)),
578 "Console" => Some(console::construct(args)),
579 "Serializer" | "DefaultSerializer" | "Deserializer" | "DefaultDeserializer" => {
580 Some(v8::construct(name, args))
581 }
582 "Socket" | "Stream" | "Server" | "SocketAddress" | "BlockList" => {
584 net::construct(name, args)
585 }
586 "Agent" | "http.Server" => http::construct(name, args),
587 n if stream_web::is_class(n) => stream_web::construct(n, args),
589 _ => None,
590 }
591}
592
593pub fn native_tag(recv: &Value) -> Option<String> {
596 with_host(|h| match h.get(recv) {
597 Some(JsObj::Object(p)) => p.get("@@native").map(|v| h.str_of(v)),
598 _ => None,
599 })
600}
601
602pub fn has_to_json(tag: &str) -> bool {
606 matches!(tag, "Buffer" | "Date" | "URL" | "MIMEType" | "MIMEParams")
607}
608
609pub fn instance_has_method(tag: &str, name: &str) -> bool {
614 let mut ctor = Some(tag);
618 while let Some(t) = ctor {
619 let (base, emitter) = instance_method_lists(t);
620 if base.contains(&name) || emitter.contains(&name) {
621 return true;
622 }
623 ctor = native_parent(t);
624 }
625 false
626}
627
628pub fn native_parent(ctor: &str) -> Option<&'static str> {
645 match ctor {
646 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" => Some("Stream"),
647 "Stream" => Some("EventEmitter"),
648 "SecretKeyObject" | "AsymmetricKeyObject" => Some("KeyObject"),
656 "PublicKeyObject" | "PrivateKeyObject" => Some("AsymmetricKeyObject"),
657 _ => None,
658 }
659}
660
661pub fn instance_late_methods(tag: &str) -> &'static [&'static str] {
694 match tag {
695 "URL" => &["toJSON"],
696 _ => &[],
697 }
698}
699
700pub fn instance_members_enumerable(tag: &str) -> bool {
701 !matches!(
702 tag,
703 "Script"
704 | "KeyObject"
705 | "SecretKeyObject"
706 | "AsymmetricKeyObject"
707 | "PublicKeyObject"
708 | "PrivateKeyObject"
709 )
710}
711
712pub fn instance_accessor_written(tag: &str, key: &str, recv: &Value) {
713 if tag == "URL" {
714 match key {
716 "href" => url::reparse(recv),
717 "host" => url::split_host(recv),
718 _ => url::refresh(recv),
719 }
720 }
721}
722
723pub fn instance_accessors(tag: &str) -> (&'static [(&'static str, bool)], &'static str) {
724 match tag {
725 "AbortController" => (&[("signal", false)], "AbortController"),
726 "AbortSignal" => (
727 &[("aborted", false), ("reason", false), ("onabort", true)],
728 "AbortSignal",
729 ),
730 "KeyObject" => (&[("type", false)], "KeyObject"),
731 "SecretKeyObject" => (&[("symmetricKeySize", false)], ""),
732 "AsymmetricKeyObject" => (
733 &[
734 ("asymmetricKeyType", false),
735 ("asymmetricKeyDetails", false),
736 ],
737 "",
738 ),
739 "TextEncoder" => (&[("encoding", false)], "TextEncoder"),
740 "URL" => (
742 &[
743 ("href", true),
744 ("origin", false),
745 ("protocol", true),
746 ("username", true),
747 ("password", true),
748 ("host", true),
749 ("hostname", true),
750 ("port", true),
751 ("pathname", true),
752 ("search", true),
753 ("searchParams", false),
754 ("hash", true),
755 ],
756 "URL",
757 ),
758 "TextDecoder" => (
759 &[("encoding", false), ("fatal", false), ("ignoreBOM", false)],
760 "TextDecoder",
761 ),
762 _ => (&[], ""),
763 }
764}
765
766pub fn instance_method_lists(tag: &str) -> (&'static [&'static str], &'static [&'static str]) {
767 const EMITTER: &[&str] = events::METHODS;
771 let base: &[&str] = match tag {
772 "Timeout" => timers::TIMEOUT_METHODS,
773 "IntervalIterator" => timers::INTERVAL_METHODS,
774 "CollectionIterator" => &["next", "@@iterator"],
775 "IteratorHelper" => iterator::METHODS,
776 "Immediate" => timers::IMMEDIATE_METHODS,
777 "Server" => &["listen", "close", "address"],
778 "Socket" => &[
779 "write",
780 "end",
781 "destroy",
782 "pause",
783 "resume",
784 "setEncoding",
785 "setKeepAlive",
786 "setNoDelay",
787 "setTimeout",
788 "ref",
789 "unref",
790 "connect",
791 ],
792 "ServerResponse" => &[
793 "writeHead",
794 "setHeader",
795 "getHeader",
796 "getHeaderNames",
797 "getHeaders",
798 "hasHeader",
799 "removeHeader",
800 "write",
801 "end",
802 "flushHeaders",
803 ],
804 "IncomingMessage" => &["pause", "resume", "setEncoding", "destroy"],
805 "Buffer" => buffer::INSTANCE_METHODS,
806 "DataView" => typedarray::DATAVIEW_METHODS,
807 "ArrayBuffer" => &["slice", "resize", "transfer", "transferToFixedLength"],
808 "Date" => date::INSTANCE_METHODS,
809 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" | "Stream" => &[
810 "read",
811 "write",
812 "end",
813 "pipe",
814 "pause",
815 "resume",
816 "setEncoding",
817 "destroy",
818 "push",
819 ],
820 "URL" => &["toString"],
822 "AsyncLocalStorage" => async_hooks::ALS_METHODS,
823 "AsyncHook" => async_hooks::HOOK_METHODS,
824 "AsyncResource" => async_hooks::RESOURCE_METHODS,
825 "Channel" => &["subscribe", "unsubscribe", "publish"],
826 "WriteStream" => &[
827 "write",
828 "end",
829 "on",
830 "once",
831 "removeListener",
832 "cork",
833 "uncork",
834 "setEncoding",
835 ],
836 "Hash" => &["update", "digest", "copy"],
843 "Hmac" => &["update", "digest"],
844 "StringDecoder" => string_decoder::INSTANCE_METHODS,
845 "Interface" => readline::INTERFACE_METHODS,
846 "Script" => vm::SCRIPT_METHODS,
847 "URLSearchParams" => url::SEARCH_PARAMS_METHODS,
848 "UdpSocket" => dgram::SOCKET_METHODS,
849 "Worker" => worker_threads::WORKER_METHODS,
850 "MessagePort" => worker_threads::PORT_METHODS,
851 "TLSServer" => tls::SERVER_METHODS,
852 "TLSSocket" => tls::SOCKET_METHODS,
853 "HTTPSServerResponse" => https::RESPONSE_METHODS,
854 "HTTPSClientRequest" => https::CLIENT_REQUEST_METHODS,
855 "REPLServer" => repl::REPLSERVER_METHODS,
856 "ClusterWorker" => cluster::WORKER_METHODS,
857 "Domain" => domain::DOMAIN_METHODS,
858 "Tracing" => trace_events::TRACING_METHODS,
859 "Http2Server" => http2::SERVER_METHODS,
860 "Http2Stream" => http2::STREAM_METHODS,
861 "Http2Session" => http2::SESSION_METHODS,
862 "Cipheriv" | "Decipheriv" => &["update", "final", "setAutoPadding"],
863 "BlockList" => net::BLOCKLIST_METHODS,
864 "ClientRequest" => http::CLIENT_REQUEST_METHODS,
865 "Agent" => &["destroy", "getName"],
866 "Blob" | "File" => buffer::BLOB_METHODS,
867 "ReadStream" => tty::READ_STREAM_METHODS,
868 "Dirent" => fs::DIRENT_METHODS,
869 "Dir" => fs::DIR_METHODS,
870 "FSReadStream" => fs::READ_STREAM_METHODS,
871 "FSWriteStream" => fs::WRITE_STREAM_METHODS,
872 "Resolver" => dns::RESOLVER_METHODS,
873 "Histogram" => perf_hooks::HISTOGRAM_METHODS,
874 "PerformanceObserver" => perf_hooks::PERFORMANCE_OBSERVER_METHODS,
875 "PerformanceObserverEntryList" => perf_hooks::OBSERVER_ENTRY_LIST_METHODS,
876 "BroadcastChannel" => worker_threads::BROADCAST_CHANNEL_METHODS,
877 "TracingChannel" => diagnostics_channel::TRACING_CHANNEL_METHODS,
878 "Serializer" => v8::SERIALIZER_METHODS,
879 "Deserializer" => v8::DESERIALIZER_METHODS,
880 "Console" => console::CONSOLE_METHODS,
881 "ChildProcess" => child_process::CHILD_PROCESS_METHODS,
882 "Sign" => &["update", "sign"],
883 "Verify" => &["update", "verify"],
884 "KeyObject" => &["equals", "toCryptoKey"],
885 "SecretKeyObject" | "PublicKeyObject" | "PrivateKeyObject" => &["export"],
887 "DiffieHellman" => &[
888 "generateKeys",
889 "computeSecret",
890 "getPrime",
891 "getGenerator",
892 "getPublicKey",
893 "getPrivateKey",
894 "setPublicKey",
895 "setPrivateKey",
896 ],
897 "ECDH" => &[
898 "generateKeys",
899 "computeSecret",
900 "getPublicKey",
901 "getPrivateKey",
902 "setPrivateKey",
903 ],
904 "X509Certificate" => &["toString"],
905 "FinalizationRegistry" => &["register", "unregister"],
906 "MIMEType" => util::MIME_TYPE_METHODS,
907 "MIMEParams" => util::MIME_PARAMS_METHODS,
908 t if fetch::is_class(t) => fetch::methods_for(t),
909 t if stream_web::is_class(t) => stream_web::methods_for(t),
910 _ => &[],
911 };
912 let is_emitter = matches!(
913 tag,
914 "Server"
915 | "Socket"
916 | "ServerResponse"
917 | "IncomingMessage"
918 | "EventEmitter"
919 | "Readable"
920 | "Writable"
921 | "Duplex"
922 | "Transform"
923 | "PassThrough"
924 | "Stream"
925 | "UdpSocket"
926 | "Worker"
927 | "MessagePort"
928 | "TLSServer"
929 | "TLSSocket"
930 | "HTTPSServerResponse"
931 | "HTTPSClientRequest"
932 | "ClusterWorker"
933 | "Domain"
934 | "Http2Server"
935 | "Http2Stream"
936 | "Http2Session"
937 | "ClientRequest"
938 | "FSReadStream"
939 | "FSWriteStream"
940 | "ChildProcess"
941 );
942 (base, if is_emitter { EMITTER } else { &[] })
943}
944
945pub fn instance_call(
949 tag: &str,
950 recv: &Value,
951 method: &str,
952 args: Vec<Value>,
953) -> Result<Value, String> {
954 match tag {
955 "Buffer" => buffer::instance_call(recv, method, &args),
956 "Timeout" | "Immediate" => timers::instance_call(recv, method, &args),
957 "IntervalIterator" => timers::interval_call(recv, method, &args),
958 "CollectionIterator" => match method {
959 "next" => crate::builtins::collection_iterator_next(recv),
960 "@@iterator" => Ok(recv.clone()),
961 m if iterator::is_helper(m) => iterator::call(recv, m, &args),
962 _ => Err(crate::host::type_error(&format!(
963 "mapIterator.{method} is not a function"
964 ))),
965 },
966 "IteratorHelper" => match method {
967 "next" => iterator::helper_next(recv),
968 "@@iterator" => Ok(recv.clone()),
969 "return" => Ok(iterator::helper_return(recv)),
973 m if iterator::is_helper(m) => iterator::call(recv, m, &args),
974 _ => Err(crate::host::type_error(&format!(
975 "{method} is not a function"
976 ))),
977 },
978 "Date" => date::instance_call(recv, method, &args),
979 "StringDecoder" => string_decoder::instance_call(recv, method, &args),
980 "WeakRef" => typedarray::weakref_call(recv, method),
981 "FinalizationRegistry" => typedarray::finalization_registry_call(recv, method, &args),
982 "TextEncoder" => typedarray::text_encoder_call(recv, method, &args),
983 "TextDecoder" => typedarray::text_decoder_call(recv, method, &args),
984 "TypedArray" => typedarray::instance_call(recv, method, &args),
985 "DataView" => typedarray::dataview_call(recv, method, &args),
986 "ArrayBuffer" if method == "slice" => {
989 if typedarray::is_detached(recv) {
990 return Err(typedarray::detached_error(
991 "ArrayBuffer.prototype",
992 "slice",
993 true,
994 ));
995 }
996 Ok(typedarray::buffer_slice(recv, &args))
997 }
998 "ArrayBuffer" if method == "resize" => typedarray::buffer_resize(recv, &args),
999 "ArrayBuffer" if method == "transfer" => typedarray::buffer_transfer(recv, &args, false),
1000 "ArrayBuffer" if method == "transferToFixedLength" => {
1001 typedarray::buffer_transfer(recv, &args, true)
1002 }
1003 t if fetch::is_class(t) => fetch::instance_call(t, recv, method, &args),
1004 "Hash" => crypto::instance_call(recv, method, &args),
1005 "Hmac" => crypto::hmac_instance_call(recv, method, &args),
1006 "Interface" => readline::instance_call(recv, method, args),
1007 "Script" => vm::instance_call(recv, method, args),
1008 "URLSearchParams" => url::search_params_call(recv, method, &args),
1009 "UdpSocket" => dgram::instance_call(recv, method, args),
1010 "Worker" | "MessagePort" | "BroadcastChannel" => {
1011 worker_threads::instance_call(tag, recv, method, args)
1012 }
1013 "TLSServer" | "TLSSocket" => tls::instance_call(tag, recv, method, args),
1014 "HTTPSServerResponse" | "HTTPSClientRequest" => {
1015 https::instance_call(tag, recv, method, args)
1016 }
1017 "REPLServer" => repl::instance_call(recv, method, args),
1018 "ClusterWorker" => cluster::instance_call(recv, method, args),
1019 "Domain" => domain::instance_call(recv, method, args),
1020 "Tracing" => trace_events::instance_call(recv, method, args),
1021 "Http2Server" | "Http2Stream" | "Http2Session" => {
1022 http2::instance_call(tag, recv, method, args)
1023 }
1024 "EventEmitter" => events::instance_call(recv, method, args),
1025 "URL" => url::instance_call(recv, method, &args),
1026 "Stats" => fs::stats_call(recv, method),
1027 "Dirent" => fs::dirent_call(recv, method),
1028 "Dir" => fs::dir_call(recv, method, args),
1029 "FSReadStream" => fs::read_stream_call(recv, method, args),
1030 "FSWriteStream" => fs::write_stream_call(recv, method, args),
1031 "Server" | "Socket" | "BlockList" => net::instance_call(tag, recv, method, args),
1032 "IncomingMessage" | "ServerResponse" | "ClientRequest" | "Agent" => {
1033 http::instance_call(tag, recv, method, args)
1034 }
1035 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" | "Stream" => {
1036 stream::instance_call(tag, recv, method, args)
1037 }
1038 "Cipheriv" | "Decipheriv" => crypto::cipher_instance_call(tag, recv, method, &args),
1039 "Sign" | "Verify" => crypto::sign_verify_instance_call(tag, recv, method, &args),
1040 "KeyObject"
1041 | "SecretKeyObject"
1042 | "AsymmetricKeyObject"
1043 | "PublicKeyObject"
1044 | "PrivateKeyObject" => crypto::key_object_instance_call(recv, method, &args),
1045 "DiffieHellman" => crypto::dh_instance_call(recv, method, &args),
1046 "ECDH" => crypto::ecdh_instance_call(recv, method, &args),
1047 "X509Certificate" => crypto::x509_instance_call(recv, method, &args),
1048 "MIMEType" => util::mime_type_instance_call(recv, method, &args),
1049 "MIMEParams" => util::mime_params_instance_call(recv, method, &args),
1050 "Blob" | "File" => buffer::blob_call(recv, method, &args),
1051 "ReadStream" => tty::instance_call(recv, method, &args),
1052 "Resolver" => dns::resolver_instance_call(recv, method, args),
1053 "Histogram" => perf_hooks::histogram_instance_call(recv, method, &args),
1054 "PerformanceObserver" => perf_hooks::observer_instance_call(recv, method, &args),
1055 "PerformanceObserverEntryList" => perf_hooks::entry_list_instance_call(recv, method, &args),
1056 "TracingChannel" => diagnostics_channel::tracing_instance_call(recv, method, &args),
1057 "Serializer" | "Deserializer" => v8::instance_call(tag, recv, method, args),
1058 "Console" => console::instance_call(recv, method, args),
1059 "ChildProcess" => child_process::instance_call(recv, method, args),
1060 t if stream_web::is_class(t) => stream_web::instance_call(t, recv, method, args),
1061 "AsyncLocalStorage" | "AsyncHook" | "AsyncResource" => {
1062 async_hooks::instance_call(tag, recv, method, args)
1063 }
1064 "Channel" => diagnostics_channel::instance_call(recv, method, &args),
1065 "WriteStream" => process::stream_instance_call(recv, method, &args),
1066 _ => Err(crate::host::type_error(&format!(
1067 "{method} is not a function"
1068 ))),
1069 }
1070}
1071
1072pub(crate) fn received_desc(v: &Value) -> String {
1079 with_host(|h| {
1080 if matches!(v, Value::Undef) {
1081 return "undefined".to_string();
1082 }
1083 if h.is_null(v) {
1084 return "null".to_string();
1085 }
1086 let ty = h.type_of(v);
1087 if ty == "function" {
1091 return format!("function {}", h.callable_name(v));
1092 }
1093 if ty == "object" {
1094 let name = match h.ctor_name(v) {
1097 n if !n.is_empty() => n,
1098 _ => match h.get(v) {
1099 Some(JsObj::Array(_)) => "Array".into(),
1100 Some(JsObj::Map { .. }) => "Map".into(),
1101 Some(JsObj::Set { .. }) => "Set".into(),
1102 Some(JsObj::Promise { .. }) => "Promise".into(),
1103 Some(JsObj::RegExp(_)) => "RegExp".into(),
1104 Some(JsObj::Object(p)) => match p.get("@@native") {
1105 Some(t) => h.str_of(t),
1106 None => "Object".into(),
1107 },
1108 _ => "Object".into(),
1109 },
1110 };
1111 return format!("an instance of {name}");
1112 }
1113 let shown = match ty {
1114 "string" => format!("'{}'", h.str_of(v)),
1115 "bigint" => format!("{}n", h.str_of(v)),
1116 "number" if matches!(v, Value::Float(f) if *f == 0.0 && f.is_sign_negative()) => {
1117 "-0".to_string()
1118 }
1119 _ => h.str_of(v),
1120 };
1121 format!("type {ty} ({shown})")
1122 })
1123}
1124
1125pub(crate) fn arg_str(args: &[Value], i: usize) -> String {
1127 with_host(|h| args.get(i).map(|v| h.str_of(v)).unwrap_or_default())
1128}
1129
1130pub(crate) fn arg_num(args: &[Value], i: usize) -> f64 {
1132 with_host(|h| args.get(i).map(|v| h.to_number(v)).unwrap_or(f64::NAN))
1133}
1134
1135pub(crate) fn to_hex(bytes: &[u8]) -> String {
1137 let mut s = String::with_capacity(bytes.len() * 2);
1138 for b in bytes {
1139 s.push(char::from_digit((b >> 4) as u32, 16).unwrap());
1140 s.push(char::from_digit((b & 0xf) as u32, 16).unwrap());
1141 }
1142 s
1143}
1144
1145pub(crate) fn from_hex(s: &str) -> Vec<u8> {
1147 let digits: Vec<u8> = s
1148 .bytes()
1149 .filter_map(|c| (c as char).to_digit(16).map(|d| d as u8))
1150 .collect();
1151 digits
1152 .chunks(2)
1153 .filter(|c| c.len() == 2)
1154 .map(|c| (c[0] << 4) | c[1])
1155 .collect()
1156}
1157
1158const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1159
1160pub(crate) fn to_base64(bytes: &[u8]) -> String {
1162 let mut out = String::new();
1163 for chunk in bytes.chunks(3) {
1164 let b = [
1165 chunk[0],
1166 *chunk.get(1).unwrap_or(&0),
1167 *chunk.get(2).unwrap_or(&0),
1168 ];
1169 let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32);
1170 out.push(B64[((n >> 18) & 63) as usize] as char);
1171 out.push(B64[((n >> 12) & 63) as usize] as char);
1172 out.push(if chunk.len() > 1 {
1173 B64[((n >> 6) & 63) as usize] as char
1174 } else {
1175 '='
1176 });
1177 out.push(if chunk.len() > 2 {
1178 B64[(n & 63) as usize] as char
1179 } else {
1180 '='
1181 });
1182 }
1183 out
1184}
1185
1186pub(crate) fn to_base64url(bytes: &[u8]) -> String {
1191 to_base64(bytes)
1192 .chars()
1193 .filter(|c| *c != '=')
1194 .map(|c| match c {
1195 '+' => '-',
1196 '/' => '_',
1197 c => c,
1198 })
1199 .collect()
1200}
1201
1202pub(crate) fn from_base64(s: &str) -> Vec<u8> {
1212 let rev = |c: u8| -> Option<u32> {
1213 let c = match c {
1214 b'-' => b'+',
1215 b'_' => b'/',
1216 c => c,
1217 };
1218 B64.iter().position(|&x| x == c).map(|p| p as u32)
1219 };
1220 let vals: Vec<u32> = s.bytes().filter_map(rev).collect();
1221 let mut out = Vec::new();
1222 for chunk in vals.chunks(4) {
1223 if chunk.len() < 2 {
1224 break;
1225 }
1226 let n = (chunk[0] << 18)
1227 | (chunk[1] << 12)
1228 | (chunk.get(2).copied().unwrap_or(0) << 6)
1229 | chunk.get(3).copied().unwrap_or(0);
1230 out.push((n >> 16) as u8);
1231 if chunk.len() > 2 {
1232 out.push((n >> 8) as u8);
1233 }
1234 if chunk.len() > 3 {
1235 out.push(n as u8);
1236 }
1237 }
1238 out
1239}