1use crate::host::{with_host, JsObj};
19use fusevm::Value;
20
21pub mod assert;
22pub mod async_hooks;
23pub mod buffer;
24pub mod child_process;
25pub mod cluster;
26pub mod console;
27pub mod crypto;
28pub mod date;
29pub mod dgram;
30pub mod diagnostics_channel;
31pub mod dns;
32pub mod domain;
33pub mod events;
34pub mod fetch;
35pub mod fs;
36pub mod fs_promises;
37pub mod http;
38pub mod http2;
39pub mod https;
40pub mod net;
41pub mod node_module;
42pub mod os;
43pub mod path;
44pub mod perf_hooks;
45pub mod process;
46pub mod punycode;
47pub mod querystring;
48pub mod readline;
49pub mod repl;
50pub mod stream;
51pub mod stream_consumers;
52pub mod stream_promises;
53pub mod stream_web;
54pub mod string_decoder;
55pub mod timers;
56pub mod tls;
57pub mod trace_events;
58pub mod tty;
59pub mod typedarray;
60pub mod url;
61pub mod url_legacy;
62pub mod util;
63pub mod util_types;
64pub mod v8;
65pub mod vm;
66pub mod worker_threads;
67pub mod zlib;
68
69pub const UNIMPLEMENTED_MODULES: &[&str] = &["inspector", "wasi"];
76
77pub fn is_unimplemented(ns: &str) -> bool {
79 UNIMPLEMENTED_MODULES.contains(&ns)
80}
81
82pub fn resolve(spec: &str) -> Option<&'static str> {
85 match spec.strip_prefix("node:").unwrap_or(spec) {
86 "fs" => Some("fs"),
87 "path" => Some("path"),
88 "os" => Some("os"),
89 "util" => Some("util"),
90 "assert" => Some("assert"),
91 "crypto" => Some("crypto"),
92 "buffer" => Some("buffer"),
93 "url" => Some("url"),
94 "process" => Some("process"),
95 "net" => Some("net"),
96 "http" => Some("http"),
97 "stream" => Some("stream"),
98 "tty" => Some("tty"),
99 "events" => Some("EventEmitter"),
102 "string_decoder" => Some("string_decoder"),
103 "zlib" => Some("zlib"),
104 "querystring" => Some("querystring"),
105 "console" => Some("console"),
106 "path/posix" => Some("path"),
110 "path/win32" => Some("path/win32"),
111 "sys" => Some("util"),
113 "assert/strict" => Some("assert"),
114 "child_process" => Some("child_process"),
115 "dns" => Some("dns"),
116 "punycode" => Some("punycode"),
117 "timers" => Some("timers"),
118 "timers/promises" => Some("timers/promises"),
119 "perf_hooks" => Some("perf_hooks"),
120 "async_hooks" => Some("async_hooks"),
121 "util/types" => Some("util/types"),
122 "diagnostics_channel" => Some("diagnostics_channel"),
123 "v8" => Some("v8"),
124 "readline" => Some("readline"),
125 "vm" => Some("vm"),
126 "fs/promises" => Some("fs/promises"),
127 "dgram" => Some("dgram"),
128 "dns/promises" => Some("dns/promises"),
129 "worker_threads" => Some("worker_threads"),
130 "tls" => Some("tls"),
131 "https" => Some("https"),
132 "repl" => Some("repl"),
133 "cluster" => Some("cluster"),
134 "domain" => Some("domain"),
135 "http2" => Some("http2"),
136 "trace_events" => Some("trace_events"),
137 "module" => Some("module"),
138 "stream/consumers" => Some("stream/consumers"),
139 "stream/promises" => Some("stream/promises"),
140 "stream/web" => Some("stream/web"),
141 other => UNIMPLEMENTED_MODULES.iter().copied().find(|&m| m == other),
142 }
143}
144
145pub fn is_method(qualified: &str) -> bool {
148 let Some((ns, m)) = qualified.split_once('.') else {
149 return qualified == "assert";
150 };
151 is_unimplemented(ns) || namespace_methods(ns).contains(&m) || namespace_ctors(ns).contains(&m)
155}
156
157pub fn namespace_methods(ns: &str) -> &'static [&'static str] {
161 match ns {
162 "fs" => fs::METHODS,
163 "path" | "path/win32" => path::METHODS,
164 "os" => os::METHODS,
165 "util" => util::METHODS,
166 "assert" | "assertStrict" => assert::METHODS,
167 "crypto" => crypto::METHODS,
168 "Buffer" => buffer::STATIC_METHODS,
169 "buffer" => buffer::MODULE_METHODS,
170 "Date" => date::STATIC_METHODS,
171 "Response" => fetch::RESPONSE_STATICS,
172 "AbortSignal" => fetch::ABORT_SIGNAL_STATICS,
173 n if typedarray::is_ctor(n) => typedarray::STATIC_METHODS,
174 "url" => url::MODULE_METHODS,
175 "net" => net::MODULE_METHODS,
176 "http" => http::MODULE_METHODS,
177 "stream" => stream::METHODS,
178 "worker_threads" => worker_threads::METHODS,
179 "zlib" => zlib::MODULE_METHODS,
180 "querystring" => querystring::METHODS,
181 "tty" => tty::METHODS,
182 "process" => process::METHODS,
183 "EventEmitter" => events::STATIC_METHODS,
184 "console" => console::METHODS,
185 "child_process" => child_process::METHODS,
186 "dns" => dns::METHODS,
187 "dns/promises" => dns::PROMISES_METHODS,
188 "punycode" => punycode::METHODS,
189 "timers" => timers::METHODS,
190 "timers/promises" => timers::PROMISES_METHODS,
191 "perf_hooks" | "performance" => perf_hooks::METHODS,
192 "async_hooks" => async_hooks::METHODS,
193 "AsyncResource" => async_hooks::RESOURCE_STATIC_METHODS,
194 "util/types" => util_types::METHODS,
195 "diagnostics_channel" => diagnostics_channel::METHODS,
196 "v8" => v8::METHODS,
197 "readline" => readline::METHODS,
198 "vm" => vm::METHODS,
199 "fs/promises" => fs_promises::METHODS,
200 "dgram" => dgram::MODULE_METHODS,
201 "tls" => tls::MODULE_METHODS,
202 "https" => https::MODULE_METHODS,
203 "repl" => repl::METHODS,
204 "cluster" => cluster::METHODS,
205 "domain" => domain::METHODS,
206 "http2" => http2::METHODS,
207 "trace_events" => trace_events::METHODS,
208 "module" => node_module::METHODS,
209 "Module" => node_module::MODULE_STATIC_METHODS,
210 "stream/consumers" => stream_consumers::METHODS,
211 "stream/promises" => stream_promises::METHODS,
212 _ => &[],
213 }
214}
215
216pub fn namespace_ctors(ns: &str) -> &'static [&'static str] {
220 match ns {
221 "buffer" => &["Buffer", "Blob", "File"],
222 "url" => &["URL", "URLSearchParams"],
223 "EventEmitter" => &["EventEmitter"],
224 "async_hooks" => &["AsyncLocalStorage", "AsyncResource"],
225 "string_decoder" => &["StringDecoder"],
226 "assert" => &["AssertionError"],
227 "console" => &["Console"],
228 "vm" => &["Script"],
229 "fs" => &["promises"],
230 "stream/web" => stream_web::CLASSES,
238 _ => &[],
239 }
240}
241
242pub fn namespace_keys(ns: &str) -> Vec<String> {
248 if ns == crate::builtins::REQUIRE_CACHE {
250 return crate::module::cache_keys();
251 }
252 let mut out: Vec<String> = namespace_ctors(ns).iter().map(|s| s.to_string()).collect();
253 for m in namespace_methods(ns) {
254 if !out.iter().any(|k| k == m) {
255 out.push((*m).to_string());
256 }
257 }
258 out
259}
260
261pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, String>> {
265 if name == "assert" {
266 return Some(assert::assert_ok(args));
267 }
268 let (ns, m) = name.split_once('.')?;
269 Some(match ns {
270 "fs" => fs::call(m, args)?,
271 "path" => path::call(path::Flavor::Posix, m, args)?,
272 "path/win32" => path::call(path::Flavor::Win32, m, args)?,
273 "os" => os::call(m, args)?,
274 "util" => util::call(m, args)?,
275 "assert" => assert::call(m, args)?,
276 "assertStrict" => assert::strict_call(m, args)?,
277 "crypto" => crypto::call(m, args)?,
278 "Buffer" => buffer::static_call(m, args)?,
279 "buffer" if m == "Buffer" => Ok(with_host(|h| h.alloc(JsObj::Builtin("Buffer".into())))),
280 "buffer" => buffer::module_call(m, args)?,
281 "Date" => date::static_call(m, args)?,
282 "Response" | "AbortSignal" => fetch::static_call(ns, m, args)?,
283 n if typedarray::is_ctor(n) => typedarray::static_call(n, m, args)?,
284 "url" if m == "URL" => Ok(with_host(|h| h.alloc(JsObj::Builtin("URL".into())))),
285 "url" => url::call(m, args)?,
286 "net" => net::call(m, args)?,
287 "http" => http::call(m, args)?,
288 "stream" => stream::call(m, args)?,
289 "worker_threads" => worker_threads::call(m, args)?,
290 "zlib" => zlib::call(m, args)?,
291 "querystring" => querystring::call(m, args)?,
292 "tty" => tty::call(m, args)?,
293 "process" => process::call(m, args)?,
294 "EventEmitter" if m == "EventEmitter" => Ok(with_host(|h| {
295 h.alloc(JsObj::Builtin("EventEmitter".into()))
296 })),
297 "EventEmitter" => events::static_call(m, args)?,
298 "console" => console::call(m, args)?,
299 "child_process" => child_process::call(m, args)?,
300 "dns" => dns::call(m, args)?,
301 "punycode" => punycode::call(m, args)?,
302 "timers" => timers::call(m, args)?,
303 "timers/promises" => timers::promises_call(m, args)?,
304 "perf_hooks" | "performance" => perf_hooks::call(m, args)?,
305 "async_hooks" => async_hooks::call(m, args)?,
306 "AsyncResource" => async_hooks::static_call(m, args)?,
307 "util/types" => util_types::call(m, args)?,
308 "diagnostics_channel" => diagnostics_channel::call(m, args)?,
309 "v8" => v8::call(m, args)?,
310 "readline" => readline::call(m, args)?,
311 "vm" => vm::call(m, args)?,
312 "fs/promises" => fs_promises::call(m, args)?,
313 "dgram" => dgram::call(m, args)?,
314 "dns/promises" => match m {
317 "getServers" | "setServers" | "getDefaultResultOrder" | "setDefaultResultOrder" => {
318 dns::call(m, args)?
319 }
320 _ => {
321 let mut pm = String::from("promise");
322 let mut cs = m.chars();
323 if let Some(c) = cs.next() {
324 pm.extend(c.to_uppercase());
325 pm.push_str(cs.as_str());
326 }
327 dns::call(&pm, args)?
328 }
329 },
330 "tls" => tls::call(m, args)?,
331 "https" => https::call(m, args)?,
332 "repl" => repl::call(m, args)?,
333 "cluster" => cluster::call(m, args)?,
334 "domain" => domain::call(m, args)?,
335 "http2" => http2::call(m, args)?,
336 "trace_events" => trace_events::call(m, args)?,
337 "module" => node_module::call(m, args)?,
338 "Module" => node_module::static_call(m, args)?,
339 "stream/consumers" => stream_consumers::call(m, args)?,
340 "stream/promises" => stream_promises::call(m, args)?,
341 _ if is_unimplemented(ns) => Err(format!("Error: {ns}.{m} is not implemented in node-js")),
342 _ => return None,
343 })
344}
345
346pub fn constant(ns: &str, name: &str) -> Option<Value> {
349 match ns {
350 "path" | "path/win32" if name == "posix" => {
353 Some(with_host(|h| h.alloc(JsObj::Builtin("path".into()))))
354 }
355 "path" | "path/win32" if name == "win32" => {
356 Some(with_host(|h| h.alloc(JsObj::Builtin("path/win32".into()))))
357 }
358 "path" => path::constant(path::Flavor::Posix, name),
359 "path/win32" => path::constant(path::Flavor::Win32, name),
360 "os" => os::constant(name),
361 "Buffer" if name == "poolSize" => Some(Value::Float(65536.0)),
367 "buffer" if name == "Buffer" => {
368 Some(with_host(|h| h.alloc(JsObj::Builtin("Buffer".into()))))
369 }
370 "buffer" if matches!(name, "Blob" | "File") => {
371 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
372 }
373 "url" if name == "URL" => Some(with_host(|h| h.alloc(JsObj::Builtin("URL".into())))),
374 "net" => net::constant(name),
375 "tty" => tty::constant(name),
376 "repl" => repl::constant(name),
377 "readline" => readline::constant(name),
378 "diagnostics_channel" => diagnostics_channel::constant(name),
379 "v8" => v8::constant(name),
380 "console" if name == "Console" => {
381 Some(with_host(|h| h.alloc(JsObj::Builtin("Console".into()))))
382 }
383 "assert" if name == "AssertionError" => Some(with_host(|h| {
384 h.alloc(JsObj::Builtin("AssertionError".into()))
385 })),
386 "assert" if name == "strict" => Some(with_host(|h| {
387 h.alloc(JsObj::Builtin("assertStrict".into()))
388 })),
389 "stream" => stream::constant(name),
390 "http" => http::constant(name),
391 "string_decoder" if name == "StringDecoder" => Some(with_host(|h| {
392 h.alloc(JsObj::Builtin("StringDecoder".into()))
393 })),
394 "process" => process::constant(name),
395 "EventEmitter" if name == "EventEmitter" => Some(with_host(|h| {
396 h.alloc(JsObj::Builtin("EventEmitter".into()))
397 })),
398 "perf_hooks" | "performance" => perf_hooks::constant(name),
399 "dns" => dns::constant(name),
400 "punycode" => punycode::constant(name),
401 "async_hooks" if matches!(name, "AsyncLocalStorage" | "AsyncResource") => {
402 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
403 }
404 "vm" if name == "Script" => Some(with_host(|h| h.alloc(JsObj::Builtin("Script".into())))),
405 "url" if name == "URLSearchParams" => Some(with_host(|h| {
406 h.alloc(JsObj::Builtin("URLSearchParams".into()))
407 })),
408 "fs" if name == "promises" => {
409 Some(with_host(|h| h.alloc(JsObj::Builtin("fs/promises".into()))))
410 }
411 "worker_threads" => worker_threads::constant(name),
412 "https" => https::constant(name),
413 "cluster" => cluster::constant(name),
414 "domain" => domain::constant(name),
415 "http2" => http2::constant(name),
416 "module" => node_module::constant(name),
417 "Module" => node_module::static_constant(name),
418 "stream/web" => stream_web::constant(name),
419 "util" => util::constant(name),
421 "crypto"
424 if matches!(
425 name,
426 "Sign"
427 | "Verify"
428 | "KeyObject"
429 | "DiffieHellman"
430 | "ECDH"
431 | "X509Certificate"
432 | "Hash"
433 | "Hmac"
434 | "Cipheriv"
435 | "Decipheriv"
436 ) =>
437 {
438 Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
439 }
440 _ => None,
441 }
442}
443
444pub fn construct(name: &str, args: &[Value]) -> Option<Result<Value, String>> {
448 match name {
449 "URL" => Some(url::construct(args)),
450 "EventEmitter" => Some(Ok(events::new_emitter())),
451 "Buffer" => {
460 let numeric = matches!(args.first(), Some(Value::Int(_)) | Some(Value::Float(_)))
461 && args.len() == 1;
462 let m = if numeric { "alloc" } else { "from" };
463 Some(buffer::static_call(m, args).unwrap_or(Ok(Value::Undef)))
464 }
465 "Date" => Some(date::construct(args)),
466 "StringDecoder" => Some(string_decoder::construct(args)),
467 "WeakRef" => Some(typedarray::construct_weakref(args)),
468 "FinalizationRegistry" => Some(typedarray::construct_finalization_registry(args)),
469 n if fetch::is_class(n) => fetch::construct(n, args),
470 "TextEncoder" => Some(typedarray::construct_text_encoder()),
471 "TextDecoder" => Some(typedarray::construct_text_decoder(args)),
472 n if typedarray::is_ctor(n) => Some(typedarray::construct(n, args)),
473 n if stream::is_class(n) => Some(Ok(stream::construct(n, args))),
474 "AsyncLocalStorage" | "AsyncResource" => async_hooks::construct(name, args),
475 "Script" => Some(vm::construct(args)),
476 "URLSearchParams" => Some(url::construct_search_params(args)),
477 "Worker" => Some(worker_threads::construct_worker(args)),
478 "Domain" => Some(domain::construct(args)),
479 "Tracing" => Some(trace_events::construct(args)),
480 "Blob" => Some(buffer::construct_blob(args)),
481 "File" => Some(buffer::construct_file(args)),
482 "AssertionError" => Some(Ok(assert::construct_assertion_error(args))),
483 "X509Certificate" => Some(crypto::construct_x509(args)),
484 "MIMEType" => Some(util::construct_mime_type(args)),
485 "MIMEParams" => Some(util::construct_mime_params(args)),
486 "Resolver" => Some(Ok(dns::construct_resolver(args))),
487 "ReadStream" | "WriteStream" => Some(Ok(tty::construct(name, args))),
488 "MessageChannel" => Some(worker_threads::construct_message_channel(args)),
489 "BroadcastChannel" => Some(worker_threads::construct_broadcast_channel(args)),
490 "PerformanceObserver" => Some(perf_hooks::construct(name, args)),
491 "REPLServer" | "Recoverable" => Some(repl::construct(name, args)),
492 "Interface" => Some(readline::construct(args)),
493 "Console" => Some(console::construct(args)),
494 "Serializer" | "DefaultSerializer" | "Deserializer" | "DefaultDeserializer" => {
495 Some(v8::construct(name, args))
496 }
497 "Socket" | "Stream" | "Server" | "SocketAddress" | "BlockList" => {
499 net::construct(name, args)
500 }
501 "Agent" | "http.Server" => http::construct(name, args),
502 n if stream_web::is_class(n) => stream_web::construct(n, args),
504 _ => None,
505 }
506}
507
508pub fn native_tag(recv: &Value) -> Option<String> {
511 with_host(|h| match h.get(recv) {
512 Some(JsObj::Object(p)) => p.get("@@native").map(|v| h.str_of(v)),
513 _ => None,
514 })
515}
516
517pub fn has_to_json(tag: &str) -> bool {
521 matches!(tag, "Buffer" | "Date" | "URL" | "MIMEType" | "MIMEParams")
522}
523
524pub fn instance_has_method(tag: &str, name: &str) -> bool {
529 let (base, emitter) = instance_method_lists(tag);
530 base.contains(&name) || emitter.contains(&name)
531}
532
533pub fn instance_method_lists(tag: &str) -> (&'static [&'static str], &'static [&'static str]) {
542 const EMITTER: &[&str] = events::METHODS;
546 let base: &[&str] = match tag {
547 "Timeout" => timers::TIMEOUT_METHODS,
548 "Immediate" => timers::IMMEDIATE_METHODS,
549 "Server" => &["listen", "close", "address"],
550 "Socket" => &[
551 "write",
552 "end",
553 "destroy",
554 "pause",
555 "resume",
556 "setEncoding",
557 "setKeepAlive",
558 "setNoDelay",
559 "setTimeout",
560 "ref",
561 "unref",
562 "connect",
563 ],
564 "ServerResponse" => &[
565 "writeHead",
566 "setHeader",
567 "getHeader",
568 "getHeaderNames",
569 "getHeaders",
570 "hasHeader",
571 "removeHeader",
572 "write",
573 "end",
574 "flushHeaders",
575 ],
576 "IncomingMessage" => &["pause", "resume", "setEncoding", "destroy"],
577 "Buffer" => buffer::INSTANCE_METHODS,
578 "Date" => date::INSTANCE_METHODS,
579 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" | "Stream" => &[
580 "read",
581 "write",
582 "end",
583 "pipe",
584 "pause",
585 "resume",
586 "setEncoding",
587 "destroy",
588 "push",
589 ],
590 "URL" => &["toString", "toJSON"],
591 "AsyncLocalStorage" => async_hooks::ALS_METHODS,
592 "AsyncHook" => async_hooks::HOOK_METHODS,
593 "AsyncResource" => async_hooks::RESOURCE_METHODS,
594 "Channel" => &["subscribe", "unsubscribe", "publish"],
595 "WriteStream" => &[
596 "write",
597 "end",
598 "on",
599 "once",
600 "removeListener",
601 "cork",
602 "uncork",
603 "setEncoding",
604 ],
605 "Hash" | "Hmac" => &["update", "digest"],
611 "StringDecoder" => string_decoder::INSTANCE_METHODS,
612 "Interface" => readline::INTERFACE_METHODS,
613 "Script" => vm::SCRIPT_METHODS,
614 "URLSearchParams" => url::SEARCH_PARAMS_METHODS,
615 "UdpSocket" => dgram::SOCKET_METHODS,
616 "Worker" => worker_threads::WORKER_METHODS,
617 "MessagePort" => worker_threads::PORT_METHODS,
618 "TLSServer" => tls::SERVER_METHODS,
619 "TLSSocket" => tls::SOCKET_METHODS,
620 "HTTPSServerResponse" => https::RESPONSE_METHODS,
621 "HTTPSClientRequest" => https::CLIENT_REQUEST_METHODS,
622 "REPLServer" => repl::REPLSERVER_METHODS,
623 "ClusterWorker" => cluster::WORKER_METHODS,
624 "Domain" => domain::DOMAIN_METHODS,
625 "Tracing" => trace_events::TRACING_METHODS,
626 "Http2Server" => http2::SERVER_METHODS,
627 "Http2Stream" => http2::STREAM_METHODS,
628 "Http2Session" => http2::SESSION_METHODS,
629 "Cipheriv" | "Decipheriv" => &["update", "final", "setAutoPadding"],
630 "BlockList" => net::BLOCKLIST_METHODS,
631 "ClientRequest" => http::CLIENT_REQUEST_METHODS,
632 "Agent" => &["destroy", "getName"],
633 "Blob" | "File" => buffer::BLOB_METHODS,
634 "ReadStream" => tty::READ_STREAM_METHODS,
635 "Dirent" => fs::DIRENT_METHODS,
636 "Dir" => fs::DIR_METHODS,
637 "FSReadStream" => fs::READ_STREAM_METHODS,
638 "FSWriteStream" => fs::WRITE_STREAM_METHODS,
639 "Resolver" => dns::RESOLVER_METHODS,
640 "Histogram" => perf_hooks::HISTOGRAM_METHODS,
641 "PerformanceObserver" => perf_hooks::PERFORMANCE_OBSERVER_METHODS,
642 "PerformanceObserverEntryList" => perf_hooks::OBSERVER_ENTRY_LIST_METHODS,
643 "BroadcastChannel" => worker_threads::BROADCAST_CHANNEL_METHODS,
644 "TracingChannel" => diagnostics_channel::TRACING_CHANNEL_METHODS,
645 "Serializer" => v8::SERIALIZER_METHODS,
646 "Deserializer" => v8::DESERIALIZER_METHODS,
647 "Console" => console::CONSOLE_METHODS,
648 "ChildProcess" => child_process::CHILD_PROCESS_METHODS,
649 "Sign" => &["update", "sign"],
650 "Verify" => &["update", "verify"],
651 "KeyObject" => &["export", "equals"],
652 "DiffieHellman" => &[
653 "generateKeys",
654 "computeSecret",
655 "getPrime",
656 "getGenerator",
657 "getPublicKey",
658 "getPrivateKey",
659 "setPublicKey",
660 "setPrivateKey",
661 ],
662 "ECDH" => &[
663 "generateKeys",
664 "computeSecret",
665 "getPublicKey",
666 "getPrivateKey",
667 "setPrivateKey",
668 ],
669 "X509Certificate" => &["toString"],
670 "FinalizationRegistry" => &["register", "unregister"],
671 "MIMEType" => util::MIME_TYPE_METHODS,
672 "MIMEParams" => util::MIME_PARAMS_METHODS,
673 t if fetch::is_class(t) => fetch::methods_for(t),
674 t if stream_web::is_class(t) => stream_web::methods_for(t),
675 _ => &[],
676 };
677 let is_emitter = matches!(
678 tag,
679 "Server"
680 | "Socket"
681 | "ServerResponse"
682 | "IncomingMessage"
683 | "EventEmitter"
684 | "Readable"
685 | "Writable"
686 | "Duplex"
687 | "Transform"
688 | "PassThrough"
689 | "Stream"
690 | "UdpSocket"
691 | "Worker"
692 | "MessagePort"
693 | "TLSServer"
694 | "TLSSocket"
695 | "HTTPSServerResponse"
696 | "HTTPSClientRequest"
697 | "ClusterWorker"
698 | "Domain"
699 | "Http2Server"
700 | "Http2Stream"
701 | "Http2Session"
702 | "ClientRequest"
703 | "FSReadStream"
704 | "FSWriteStream"
705 | "ChildProcess"
706 );
707 (base, if is_emitter { EMITTER } else { &[] })
708}
709
710pub fn instance_call(
714 tag: &str,
715 recv: &Value,
716 method: &str,
717 args: Vec<Value>,
718) -> Result<Value, String> {
719 match tag {
720 "Buffer" => buffer::instance_call(recv, method, &args),
721 "Timeout" | "Immediate" => timers::instance_call(recv, method, &args),
722 "Date" => date::instance_call(recv, method, &args),
723 "StringDecoder" => string_decoder::instance_call(recv, method, &args),
724 "WeakRef" => typedarray::weakref_call(recv, method),
725 "FinalizationRegistry" => typedarray::finalization_registry_call(recv, method, &args),
726 "TextEncoder" => typedarray::text_encoder_call(recv, method, &args),
727 "TextDecoder" => typedarray::text_decoder_call(recv, method, &args),
728 "TypedArray" => typedarray::instance_call(recv, method, &args),
729 t if fetch::is_class(t) => fetch::instance_call(t, recv, method, &args),
730 "Hash" => crypto::instance_call(recv, method, &args),
731 "Hmac" => crypto::hmac_instance_call(recv, method, &args),
732 "Interface" => readline::instance_call(recv, method, args),
733 "Script" => vm::instance_call(recv, method, args),
734 "URLSearchParams" => url::search_params_call(recv, method, &args),
735 "UdpSocket" => dgram::instance_call(recv, method, args),
736 "Worker" | "MessagePort" | "BroadcastChannel" => {
737 worker_threads::instance_call(tag, recv, method, args)
738 }
739 "TLSServer" | "TLSSocket" => tls::instance_call(tag, recv, method, args),
740 "HTTPSServerResponse" | "HTTPSClientRequest" => {
741 https::instance_call(tag, recv, method, args)
742 }
743 "REPLServer" => repl::instance_call(recv, method, args),
744 "ClusterWorker" => cluster::instance_call(recv, method, args),
745 "Domain" => domain::instance_call(recv, method, args),
746 "Tracing" => trace_events::instance_call(recv, method, args),
747 "Http2Server" | "Http2Stream" | "Http2Session" => {
748 http2::instance_call(tag, recv, method, args)
749 }
750 "EventEmitter" => events::instance_call(recv, method, args),
751 "URL" => url::instance_call(recv, method, &args),
752 "Stats" => fs::stats_call(recv, method),
753 "Dirent" => fs::dirent_call(recv, method),
754 "Dir" => fs::dir_call(recv, method, args),
755 "FSReadStream" => fs::read_stream_call(recv, method, args),
756 "FSWriteStream" => fs::write_stream_call(recv, method, args),
757 "Server" | "Socket" | "BlockList" => net::instance_call(tag, recv, method, args),
758 "IncomingMessage" | "ServerResponse" | "ClientRequest" | "Agent" => {
759 http::instance_call(tag, recv, method, args)
760 }
761 "Readable" | "Writable" | "Duplex" | "Transform" | "PassThrough" | "Stream" => {
762 stream::instance_call(tag, recv, method, args)
763 }
764 "Cipheriv" | "Decipheriv" => crypto::cipher_instance_call(tag, recv, method, &args),
765 "Sign" | "Verify" => crypto::sign_verify_instance_call(tag, recv, method, &args),
766 "KeyObject" => crypto::key_object_instance_call(recv, method, &args),
767 "DiffieHellman" => crypto::dh_instance_call(recv, method, &args),
768 "ECDH" => crypto::ecdh_instance_call(recv, method, &args),
769 "X509Certificate" => crypto::x509_instance_call(recv, method, &args),
770 "MIMEType" => util::mime_type_instance_call(recv, method, &args),
771 "MIMEParams" => util::mime_params_instance_call(recv, method, &args),
772 "Blob" | "File" => buffer::blob_call(recv, method, &args),
773 "ReadStream" => tty::instance_call(recv, method, &args),
774 "Resolver" => dns::resolver_instance_call(recv, method, args),
775 "Histogram" => perf_hooks::histogram_instance_call(recv, method, &args),
776 "PerformanceObserver" => perf_hooks::observer_instance_call(recv, method, &args),
777 "PerformanceObserverEntryList" => perf_hooks::entry_list_instance_call(recv, method, &args),
778 "TracingChannel" => diagnostics_channel::tracing_instance_call(recv, method, &args),
779 "Serializer" | "Deserializer" => v8::instance_call(tag, recv, method, args),
780 "Console" => console::instance_call(recv, method, args),
781 "ChildProcess" => child_process::instance_call(recv, method, args),
782 t if stream_web::is_class(t) => stream_web::instance_call(t, recv, method, args),
783 "AsyncLocalStorage" | "AsyncHook" | "AsyncResource" => {
784 async_hooks::instance_call(tag, recv, method, args)
785 }
786 "Channel" => diagnostics_channel::instance_call(recv, method, &args),
787 "WriteStream" => process::stream_instance_call(recv, method, &args),
788 _ => Err(crate::host::type_error(&format!(
789 "{method} is not a function"
790 ))),
791 }
792}
793
794pub(crate) fn received_desc(v: &Value) -> String {
801 with_host(|h| {
802 if matches!(v, Value::Undef) {
803 return "undefined".to_string();
804 }
805 if h.is_null(v) {
806 return "null".to_string();
807 }
808 let ty = h.type_of(v);
809 if ty == "object" || ty == "function" {
810 let name = match h.ctor_name(v) {
813 n if !n.is_empty() => n,
814 _ => match h.get(v) {
815 Some(JsObj::Array(_)) => "Array".into(),
816 Some(JsObj::Map { .. }) => "Map".into(),
817 Some(JsObj::Set { .. }) => "Set".into(),
818 Some(JsObj::Promise { .. }) => "Promise".into(),
819 Some(JsObj::RegExp(_)) => "RegExp".into(),
820 Some(JsObj::Object(p)) => match p.get("@@native") {
821 Some(t) => h.str_of(t),
822 None => "Object".into(),
823 },
824 _ => "Object".into(),
825 },
826 };
827 return format!("an instance of {name}");
828 }
829 let shown = match ty {
830 "string" => format!("'{}'", h.str_of(v)),
831 "bigint" => format!("{}n", h.str_of(v)),
832 "number" if matches!(v, Value::Float(f) if *f == 0.0 && f.is_sign_negative()) => {
833 "-0".to_string()
834 }
835 _ => h.str_of(v),
836 };
837 format!("type {ty} ({shown})")
838 })
839}
840
841pub(crate) fn arg_str(args: &[Value], i: usize) -> String {
843 with_host(|h| args.get(i).map(|v| h.str_of(v)).unwrap_or_default())
844}
845
846pub(crate) fn arg_num(args: &[Value], i: usize) -> f64 {
848 with_host(|h| args.get(i).map(|v| h.to_number(v)).unwrap_or(f64::NAN))
849}
850
851pub(crate) fn to_hex(bytes: &[u8]) -> String {
853 let mut s = String::with_capacity(bytes.len() * 2);
854 for b in bytes {
855 s.push(char::from_digit((b >> 4) as u32, 16).unwrap());
856 s.push(char::from_digit((b & 0xf) as u32, 16).unwrap());
857 }
858 s
859}
860
861pub(crate) fn from_hex(s: &str) -> Vec<u8> {
863 let digits: Vec<u8> = s
864 .bytes()
865 .filter_map(|c| (c as char).to_digit(16).map(|d| d as u8))
866 .collect();
867 digits
868 .chunks(2)
869 .filter(|c| c.len() == 2)
870 .map(|c| (c[0] << 4) | c[1])
871 .collect()
872}
873
874const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
875
876pub(crate) fn to_base64(bytes: &[u8]) -> String {
878 let mut out = String::new();
879 for chunk in bytes.chunks(3) {
880 let b = [
881 chunk[0],
882 *chunk.get(1).unwrap_or(&0),
883 *chunk.get(2).unwrap_or(&0),
884 ];
885 let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32);
886 out.push(B64[((n >> 18) & 63) as usize] as char);
887 out.push(B64[((n >> 12) & 63) as usize] as char);
888 out.push(if chunk.len() > 1 {
889 B64[((n >> 6) & 63) as usize] as char
890 } else {
891 '='
892 });
893 out.push(if chunk.len() > 2 {
894 B64[(n & 63) as usize] as char
895 } else {
896 '='
897 });
898 }
899 out
900}
901
902pub(crate) fn to_base64url(bytes: &[u8]) -> String {
907 to_base64(bytes)
908 .chars()
909 .filter(|c| *c != '=')
910 .map(|c| match c {
911 '+' => '-',
912 '/' => '_',
913 c => c,
914 })
915 .collect()
916}
917
918pub(crate) fn from_base64(s: &str) -> Vec<u8> {
928 let rev = |c: u8| -> Option<u32> {
929 let c = match c {
930 b'-' => b'+',
931 b'_' => b'/',
932 c => c,
933 };
934 B64.iter().position(|&x| x == c).map(|p| p as u32)
935 };
936 let vals: Vec<u32> = s.bytes().filter_map(rev).collect();
937 let mut out = Vec::new();
938 for chunk in vals.chunks(4) {
939 if chunk.len() < 2 {
940 break;
941 }
942 let n = (chunk[0] << 18)
943 | (chunk[1] << 12)
944 | (chunk.get(2).copied().unwrap_or(0) << 6)
945 | chunk.get(3).copied().unwrap_or(0);
946 out.push((n >> 16) as u8);
947 if chunk.len() > 2 {
948 out.push((n >> 8) as u8);
949 }
950 if chunk.len() > 3 {
951 out.push(n as u8);
952 }
953 }
954 out
955}