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 "addListener",
832 "prependListener",
833 "removeListener",
834 "off",
835 "removeAllListeners",
836 "listenerCount",
837 "cork",
838 "uncork",
839 "setEncoding",
840 "resume",
841 "pause",
842 "read",
843 "pipe",
844 "@@asyncIterator",
845 ],
846 "StdinIterator" => process::STDIN_ITERATOR_METHODS,
847 "Hash" => &["update", "digest", "copy"],
854 "Hmac" => &["update", "digest"],
855 "StringDecoder" => string_decoder::INSTANCE_METHODS,
856 "Interface" => readline::INTERFACE_METHODS,
857 "ReadlineIterator" => readline::ITERATOR_METHODS,
858 "Script" => vm::SCRIPT_METHODS,
859 "URLSearchParams" => url::SEARCH_PARAMS_METHODS,
860 "UdpSocket" => dgram::SOCKET_METHODS,
861 "Worker" => worker_threads::WORKER_METHODS,
862 "MessagePort" => worker_threads::PORT_METHODS,
863 "TLSServer" => tls::SERVER_METHODS,
864 "TLSSocket" => tls::SOCKET_METHODS,
865 "HTTPSServerResponse" => https::RESPONSE_METHODS,
866 "HTTPSClientRequest" => https::CLIENT_REQUEST_METHODS,
867 "REPLServer" => repl::REPLSERVER_METHODS,
868 "ClusterWorker" => cluster::WORKER_METHODS,
869 "Domain" => domain::DOMAIN_METHODS,
870 "Tracing" => trace_events::TRACING_METHODS,
871 "Http2Server" => http2::SERVER_METHODS,
872 "Http2Stream" => http2::STREAM_METHODS,
873 "Http2Session" => http2::SESSION_METHODS,
874 "Cipheriv" | "Decipheriv" => &["update", "final", "setAutoPadding"],
875 "BlockList" => net::BLOCKLIST_METHODS,
876 "ClientRequest" => http::CLIENT_REQUEST_METHODS,
877 "Agent" => &["destroy", "getName"],
878 "Blob" | "File" => buffer::BLOB_METHODS,
879 "ReadStream" => tty::READ_STREAM_METHODS,
880 "Dirent" => fs::DIRENT_METHODS,
881 "Dir" => fs::DIR_METHODS,
882 "FSReadStream" => fs::READ_STREAM_METHODS,
883 "FSWriteStream" => fs::WRITE_STREAM_METHODS,
884 "Resolver" => dns::RESOLVER_METHODS,
885 "Histogram" => perf_hooks::HISTOGRAM_METHODS,
886 "PerformanceObserver" => perf_hooks::PERFORMANCE_OBSERVER_METHODS,
887 "PerformanceObserverEntryList" => perf_hooks::OBSERVER_ENTRY_LIST_METHODS,
888 "BroadcastChannel" => worker_threads::BROADCAST_CHANNEL_METHODS,
889 "TracingChannel" => diagnostics_channel::TRACING_CHANNEL_METHODS,
890 "Serializer" => v8::SERIALIZER_METHODS,
891 "Deserializer" => v8::DESERIALIZER_METHODS,
892 "Console" => console::CONSOLE_METHODS,
893 "ChildProcess" => child_process::CHILD_PROCESS_METHODS,
894 "ChildPipe" => child_process::CHILD_PIPE_METHODS,
895 "Sign" => &["update", "sign"],
896 "Verify" => &["update", "verify"],
897 "KeyObject" => &["equals", "toCryptoKey"],
898 "SecretKeyObject" | "PublicKeyObject" | "PrivateKeyObject" => &["export"],
900 "DiffieHellman" => &[
901 "generateKeys",
902 "computeSecret",
903 "getPrime",
904 "getGenerator",
905 "getPublicKey",
906 "getPrivateKey",
907 "setPublicKey",
908 "setPrivateKey",
909 ],
910 "ECDH" => &[
911 "generateKeys",
912 "computeSecret",
913 "getPublicKey",
914 "getPrivateKey",
915 "setPrivateKey",
916 ],
917 "X509Certificate" => &["toString"],
918 "FinalizationRegistry" => &["register", "unregister"],
919 "MIMEType" => util::MIME_TYPE_METHODS,
920 "MIMEParams" => util::MIME_PARAMS_METHODS,
921 t if fetch::is_class(t) => fetch::methods_for(t),
922 t if stream_web::is_class(t) => stream_web::methods_for(t),
923 _ => &[],
924 };
925 let is_emitter = matches!(
926 tag,
927 "Server"
928 | "Socket"
929 | "ServerResponse"
930 | "IncomingMessage"
931 | "EventEmitter"
932 | "Readable"
933 | "Writable"
934 | "Duplex"
935 | "Transform"
936 | "PassThrough"
937 | "Stream"
938 | "UdpSocket"
939 | "Worker"
940 | "MessagePort"
941 | "TLSServer"
942 | "TLSSocket"
943 | "HTTPSServerResponse"
944 | "HTTPSClientRequest"
945 | "ClusterWorker"
946 | "Domain"
947 | "Http2Server"
948 | "Http2Stream"
949 | "Http2Session"
950 | "ClientRequest"
951 | "FSReadStream"
952 | "FSWriteStream"
953 | "ChildProcess"
954 | "ChildPipe"
955 );
956 (base, if is_emitter { EMITTER } else { &[] })
957}
958
959pub fn instance_call(
963 tag: &str,
964 recv: &Value,
965 method: &str,
966 args: Vec<Value>,
967) -> Result<Value, String> {
968 match tag {
969 "Buffer" => buffer::instance_call(recv, method, &args),
970 "Timeout" | "Immediate" => timers::instance_call(recv, method, &args),
971 "IntervalIterator" => timers::interval_call(recv, method, &args),
972 "CollectionIterator" => match method {
973 "next" => crate::builtins::collection_iterator_next(recv),
974 "@@iterator" => Ok(recv.clone()),
975 m if iterator::is_helper(m) => iterator::call(recv, m, &args),
976 _ => Err(crate::host::type_error(&format!(
977 "mapIterator.{method} is not a function"
978 ))),
979 },
980 "IteratorHelper" => match method {
981 "next" => iterator::helper_next(recv),
982 "@@iterator" => Ok(recv.clone()),
983 "return" => Ok(iterator::helper_return(recv)),
987 m if iterator::is_helper(m) => iterator::call(recv, m, &args),
988 _ => Err(crate::host::type_error(&format!(
989 "{method} is not a function"
990 ))),
991 },
992 "Date" => date::instance_call(recv, method, &args),
993 "StringDecoder" => string_decoder::instance_call(recv, method, &args),
994 "WeakRef" => typedarray::weakref_call(recv, method),
995 "FinalizationRegistry" => typedarray::finalization_registry_call(recv, method, &args),
996 "TextEncoder" => typedarray::text_encoder_call(recv, method, &args),
997 "TextDecoder" => typedarray::text_decoder_call(recv, method, &args),
998 "TypedArray" => typedarray::instance_call(recv, method, &args),
999 "DataView" => typedarray::dataview_call(recv, method, &args),
1000 "ArrayBuffer" if method == "slice" => {
1003 if typedarray::is_detached(recv) {
1004 return Err(typedarray::detached_error(
1005 "ArrayBuffer.prototype",
1006 "slice",
1007 true,
1008 ));
1009 }
1010 Ok(typedarray::buffer_slice(recv, &args))
1011 }
1012 "ArrayBuffer" if method == "resize" => typedarray::buffer_resize(recv, &args),
1013 "ArrayBuffer" if method == "transfer" => typedarray::buffer_transfer(recv, &args, false),
1014 "ArrayBuffer" if method == "transferToFixedLength" => {
1015 typedarray::buffer_transfer(recv, &args, true)
1016 }
1017 t if fetch::is_class(t) => fetch::instance_call(t, recv, method, &args),
1018 "Hash" => crypto::instance_call(recv, method, &args),
1019 "Hmac" => crypto::hmac_instance_call(recv, method, &args),
1020 "Interface" => readline::instance_call(recv, method, args),
1021 "ReadlineIterator" => readline::iterator_call(recv, method),
1022 "Script" => vm::instance_call(recv, method, args),
1023 "URLSearchParams" => url::search_params_call(recv, method, &args),
1024 "UdpSocket" => dgram::instance_call(recv, method, args),
1025 "Worker" | "MessagePort" | "BroadcastChannel" => {
1026 worker_threads::instance_call(tag, recv, method, args)
1027 }
1028 "TLSServer" | "TLSSocket" => tls::instance_call(tag, recv, method, args),
1029 "HTTPSServerResponse" | "HTTPSClientRequest" => {
1030 https::instance_call(tag, recv, method, args)
1031 }
1032 "REPLServer" => repl::instance_call(recv, method, args),
1033 "ClusterWorker" => cluster::instance_call(recv, method, args),
1034 "Domain" => domain::instance_call(recv, method, args),
1035 "Tracing" => trace_events::instance_call(recv, method, args),
1036 "Http2Server" | "Http2Stream" | "Http2Session" => {
1037 http2::instance_call(tag, recv, method, args)
1038 }
1039 "EventEmitter" => events::instance_call(recv, method, args),
1040 "URL" => url::instance_call(recv, method, &args),
1041 "Stats" => fs::stats_call(recv, method),
1042 "Dirent" => fs::dirent_call(recv, method),
1043 "Dir" => fs::dir_call(recv, method, args),
1044 "FSReadStream" => fs::read_stream_call(recv, method, args),
1045 "FSWriteStream" => fs::write_stream_call(recv, method, args),
1046 "Server" | "Socket" | "BlockList" => net::instance_call(tag, recv, method, args),
1047 "IncomingMessage" | "ServerResponse" | "ClientRequest" | "Agent" => {
1048 http::instance_call(tag, recv, method, args)
1049 }
1050 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" | "Stream" => {
1051 stream::instance_call(tag, recv, method, args)
1052 }
1053 "Cipheriv" | "Decipheriv" => crypto::cipher_instance_call(tag, recv, method, &args),
1054 "Sign" | "Verify" => crypto::sign_verify_instance_call(tag, recv, method, &args),
1055 "KeyObject"
1056 | "SecretKeyObject"
1057 | "AsymmetricKeyObject"
1058 | "PublicKeyObject"
1059 | "PrivateKeyObject" => crypto::key_object_instance_call(recv, method, &args),
1060 "DiffieHellman" => crypto::dh_instance_call(recv, method, &args),
1061 "ECDH" => crypto::ecdh_instance_call(recv, method, &args),
1062 "X509Certificate" => crypto::x509_instance_call(recv, method, &args),
1063 "MIMEType" => util::mime_type_instance_call(recv, method, &args),
1064 "MIMEParams" => util::mime_params_instance_call(recv, method, &args),
1065 "Blob" | "File" => buffer::blob_call(recv, method, &args),
1066 "ReadStream" => tty::instance_call(recv, method, &args),
1067 "Resolver" => dns::resolver_instance_call(recv, method, args),
1068 "Histogram" => perf_hooks::histogram_instance_call(recv, method, &args),
1069 "PerformanceObserver" => perf_hooks::observer_instance_call(recv, method, &args),
1070 "PerformanceObserverEntryList" => perf_hooks::entry_list_instance_call(recv, method, &args),
1071 "TracingChannel" => diagnostics_channel::tracing_instance_call(recv, method, &args),
1072 "Serializer" | "Deserializer" => v8::instance_call(tag, recv, method, args),
1073 "Console" => console::instance_call(recv, method, args),
1074 "ChildProcess" => child_process::instance_call(recv, method, args),
1075 "ChildPipe" => child_process::pipe_call(recv, method, args),
1076 t if stream_web::is_class(t) => stream_web::instance_call(t, recv, method, args),
1077 "AsyncLocalStorage" | "AsyncHook" | "AsyncResource" => {
1078 async_hooks::instance_call(tag, recv, method, args)
1079 }
1080 "Channel" => diagnostics_channel::instance_call(recv, method, &args),
1081 "WriteStream" => process::stream_instance_call(recv, method, &args),
1082 "StdinIterator" => process::stdin_iterator_call(recv, method),
1083 _ => Err(crate::host::type_error(&format!(
1084 "{method} is not a function"
1085 ))),
1086 }
1087}
1088
1089pub(crate) fn received_desc(v: &Value) -> String {
1096 with_host(|h| {
1097 if matches!(v, Value::Undef) {
1098 return "undefined".to_string();
1099 }
1100 if h.is_null(v) {
1101 return "null".to_string();
1102 }
1103 let ty = h.type_of(v);
1104 if ty == "function" {
1108 return format!("function {}", h.callable_name(v));
1109 }
1110 if ty == "object" {
1111 let name = match h.ctor_name(v) {
1114 n if !n.is_empty() => n,
1115 _ => match h.get(v) {
1116 Some(JsObj::Array(_)) => "Array".into(),
1117 Some(JsObj::Map { .. }) => "Map".into(),
1118 Some(JsObj::Set { .. }) => "Set".into(),
1119 Some(JsObj::Promise { .. }) => "Promise".into(),
1120 Some(JsObj::RegExp(_)) => "RegExp".into(),
1121 Some(JsObj::Object(p)) => match p.get("@@native") {
1122 Some(t) => h.str_of(t),
1123 None => "Object".into(),
1124 },
1125 _ => "Object".into(),
1126 },
1127 };
1128 return format!("an instance of {name}");
1129 }
1130 let shown = match ty {
1131 "string" => format!("'{}'", h.str_of(v)),
1132 "bigint" => format!("{}n", h.str_of(v)),
1133 "number" if matches!(v, Value::Float(f) if *f == 0.0 && f.is_sign_negative()) => {
1134 "-0".to_string()
1135 }
1136 _ => h.str_of(v),
1137 };
1138 format!("type {ty} ({shown})")
1139 })
1140}
1141
1142pub(crate) fn arg_str(args: &[Value], i: usize) -> String {
1144 with_host(|h| args.get(i).map(|v| h.str_of(v)).unwrap_or_default())
1145}
1146
1147pub(crate) fn arg_num(args: &[Value], i: usize) -> f64 {
1149 with_host(|h| args.get(i).map(|v| h.to_number(v)).unwrap_or(f64::NAN))
1150}
1151
1152pub(crate) fn to_hex(bytes: &[u8]) -> String {
1154 let mut s = String::with_capacity(bytes.len() * 2);
1155 for b in bytes {
1156 s.push(char::from_digit((b >> 4) as u32, 16).unwrap());
1157 s.push(char::from_digit((b & 0xf) as u32, 16).unwrap());
1158 }
1159 s
1160}
1161
1162pub(crate) fn from_hex(s: &str) -> Vec<u8> {
1164 let digits: Vec<u8> = s
1165 .bytes()
1166 .filter_map(|c| (c as char).to_digit(16).map(|d| d as u8))
1167 .collect();
1168 digits
1169 .chunks(2)
1170 .filter(|c| c.len() == 2)
1171 .map(|c| (c[0] << 4) | c[1])
1172 .collect()
1173}
1174
1175const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1176
1177pub(crate) fn to_base64(bytes: &[u8]) -> String {
1179 let mut out = String::new();
1180 for chunk in bytes.chunks(3) {
1181 let b = [
1182 chunk[0],
1183 *chunk.get(1).unwrap_or(&0),
1184 *chunk.get(2).unwrap_or(&0),
1185 ];
1186 let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32);
1187 out.push(B64[((n >> 18) & 63) as usize] as char);
1188 out.push(B64[((n >> 12) & 63) as usize] as char);
1189 out.push(if chunk.len() > 1 {
1190 B64[((n >> 6) & 63) as usize] as char
1191 } else {
1192 '='
1193 });
1194 out.push(if chunk.len() > 2 {
1195 B64[(n & 63) as usize] as char
1196 } else {
1197 '='
1198 });
1199 }
1200 out
1201}
1202
1203pub(crate) fn to_base64url(bytes: &[u8]) -> String {
1208 to_base64(bytes)
1209 .chars()
1210 .filter(|c| *c != '=')
1211 .map(|c| match c {
1212 '+' => '-',
1213 '/' => '_',
1214 c => c,
1215 })
1216 .collect()
1217}
1218
1219pub(crate) fn from_base64(s: &str) -> Vec<u8> {
1229 let rev = |c: u8| -> Option<u32> {
1230 let c = match c {
1231 b'-' => b'+',
1232 b'_' => b'/',
1233 c => c,
1234 };
1235 B64.iter().position(|&x| x == c).map(|p| p as u32)
1236 };
1237 let vals: Vec<u32> = s.bytes().filter_map(rev).collect();
1238 let mut out = Vec::new();
1239 for chunk in vals.chunks(4) {
1240 if chunk.len() < 2 {
1241 break;
1242 }
1243 let n = (chunk[0] << 18)
1244 | (chunk[1] << 12)
1245 | (chunk.get(2).copied().unwrap_or(0) << 6)
1246 | chunk.get(3).copied().unwrap_or(0);
1247 out.push((n >> 16) as u8);
1248 if chunk.len() > 2 {
1249 out.push((n >> 8) as u8);
1250 }
1251 if chunk.len() > 3 {
1252 out.push(n as u8);
1253 }
1254 }
1255 out
1256}