nodehost

package
v0.0.0-...-f725ab5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package nodehost holds the host functions the AOT-compiled Node builtins call into. A factory in pkg/node/js references a bare __bento_* callee for work that needs the Go runtime, os detail or an object inspector, and the lowerer emits a call to the matching function here. The package imports only the standard library, never pkg/engine, so an AOT binary that calls into it stays free of the interpreter the way the whole AOT path is; the interpreter's own pkg/node host layer delegates to the same functions so the two share one implementation and never drift. pkg/value depends on this package rather than the other way about, which is what lets a value helper answer os.freemem from the same measurement the interpreter reads, so nothing here may import it back. It also reaches for golang.org/x/sys, which is where the per-platform system calls the os module's numbers come from live; that is the standard library by another name and not a step toward the interpreter.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NetError

func NetError(err error, call, address string, port int) (message, props string)

NetError builds the error a failed network call raises in Node: the message the Error carries and the properties Node hangs off it, as a JSON object ready for the JavaScript side to assign.

call is the syscall Node blames, "connect" or "listen" or "bind". address and port are what the call named; pass an empty address when there is none to report, and the Go error's own text stands in for the message, since a Node message without an address is not a shape Node produces.

The address is the host the caller asked for rather than the address the resolver returned. They differ only when a name resolved and then the connect failed, which is rare enough next to the connect-refused case that reporting the name is worth more than a second lookup.

func OSArch

func OSArch() string

OSArch is os.arch(), Node's name for the processor architecture the binary was built for, which is not the name uname gives it. See OSMachine.

func OSAvailableParallelism

func OSAvailableParallelism() float64

OSAvailableParallelism is os.availableParallelism(), the count of cores this process may run on.

func OSEndianness

func OSEndianness() string

OSEndianness is os.endianness(), the byte order of the processor this binary runs on, measured rather than assumed from the architecture name.

func OSFreemem

func OSFreemem() float64

OSFreemem is os.freemem(), the memory not in use, in bytes. It is measured on each call, since it is the one number here that moves while a program runs.

func OSHomedir

func OSHomedir() string

OSHomedir is os.homedir(), the current user's home directory.

func OSHostname

func OSHostname() string

OSHostname is os.hostname(). A machine that will not answer its own name yields the empty string, which is what Node reports when the lookup fails.

func OSInfoJSON

func OSInfoJSON() string

OSInfoJSON returns the os module snapshot marshaled to a JSON string, the value the os.js factory parses back into the numbers and strings os.platform, os.cpus, and the rest report. A marshal error yields "{}", so the JavaScript JSON.parse always has a well-formed object to read.

func OSLoadavg

func OSLoadavg() [3]float64

OSLoadavg is os.loadavg(), the one, five and fifteen minute run queue averages. Windows keeps no such thing and Node reports three zeros there.

func OSMachine

func OSMachine() string

OSMachine is os.machine(), the hardware name uname reports. It is a different question from OSArch and has a different answer on a 64-bit Intel machine, where uname says x86_64 and Node's arch says x64.

func OSNetworkInterfaces

func OSNetworkInterfaces() map[string][]NetInterface

OSNetworkInterfaces is os.networkInterfaces(), the addresses of each interface keyed by its name, measured on each call because an interface can come up or go away while a program runs.

func OSPlatform

func OSPlatform() string

OSPlatform is os.platform(), Node's name for the operating system.

func OSRelease

func OSRelease() string

OSRelease is os.release(), the kernel release string.

func OSTotalmem

func OSTotalmem() float64

OSTotalmem is os.totalmem(), the machine's physical memory in bytes. It is a float64 because that is the only number JavaScript has, and it is the type the compiled program does arithmetic on; a byte count of physical memory is far inside the range a float64 counts exactly.

func OSType

func OSType() string

OSType is os.type(), the operating system name uname reports: Linux, Darwin, Windows_NT.

func OSUptime

func OSUptime() float64

OSUptime is os.uptime(), the seconds since the machine booted.

func OSVersion

func OSVersion() string

OSVersion is os.version(), the kernel version string, which on Windows is the product name instead since there is no kernel version string there.

func URLParseJSON

func URLParseJSON(input, base string) string

URLParseJSON is URLParse marshaled for the interpreter's host bridge, which moves strings and not structs. On failure it returns {"ok":false}.

Types

type CPUInfo

type CPUInfo struct {
	Model string   `json:"model"`
	Speed int      `json:"speed"`
	Times CPUTimes `json:"times"`
}

CPUInfo is one entry of the array os.cpus() answers: the processor's model name, its clock in megahertz, and the time its core has spent in each of the scheduler's states since the machine booted.

func OSCPUs

func OSCPUs() []CPUInfo

OSCPUs is os.cpus(), one entry per core the machine has. It is measured on each call, since the times in it climb while a program runs and a program that wants a utilization figure reads it twice and subtracts.

type CPUTimes

type CPUTimes struct {
	User int `json:"user"`
	Nice int `json:"nice"`
	Sys  int `json:"sys"`
	Idle int `json:"idle"`
	IRQ  int `json:"irq"`
}

CPUTimes counts milliseconds, which is the unit Node reports and not the unit any kernel keeps. Every platform counts in something else, ticks on Linux and hundreds of nanoseconds on Windows, and converts on the way out.

type NetInterface

type NetInterface struct {
	Address  string `json:"address"`
	Netmask  string `json:"netmask"`
	Family   string `json:"family"`
	MAC      string `json:"mac"`
	Internal bool   `json:"internal"`
	CIDR     string `json:"cidr"`
}

NetInterface is one address of one interface, an entry of the arrays os.networkInterfaces() keys by interface name. An interface with four addresses is four of these under one key, which is the shape Node reports.

type URLComponents

type URLComponents struct {
	OK       bool   `json:"ok"`
	Href     string `json:"href"`
	Protocol string `json:"protocol"`
	Username string `json:"username"`
	Password string `json:"password"`
	Host     string `json:"host"`
	Hostname string `json:"hostname"`
	Port     string `json:"port"`
	Pathname string `json:"pathname"`
	Search   string `json:"search"`
	Hash     string `json:"hash"`
	Origin   string `json:"origin"`
}

URLComponents is a parsed URL projected onto the WHATWG property set. The JSON tags are the interpreter's wire format, so they are part of the contract with pkg/node/js/url.js.

func URLParse

func URLParse(input, base string) (URLComponents, bool)

URLParse resolves input against an optional base and returns the parsed WHATWG components. base is the empty string when there is no base. ok is false for an input that is not a valid absolute URL, which both callers turn into the TypeError the WHATWG contract requires: an invalid URL is a hard error, not a null result.

Parsing a URL correctly (percent encoding, IDNA hosts, default ports, base resolution) is a lot of surface to reimplement, and Go's net/url already does it, so this is the one implementation. The interpreter reaches it through URLParseJSON below; the AOT path calls it directly, since it has no bridge to cross and no reason to pay for a marshal and a parse to move a struct across a package boundary.

type UVError

type UVError struct {
	// Code is libuv's name for the failure, the string err.code carries: ENOENT,
	// EEXIST, ENOTEMPTY and so on, or UNKNOWN for a failure no table names.
	Code string
	// Errno is libuv's number for that code, the value err.errno carries. It is
	// always negative.
	Errno int
	// Desc is libuv's description of the code, the middle of Node's message:
	// "no such file or directory" in "ENOENT: no such file or directory, open
	// '/nope'". For an unclassified failure it is the Go error's own text, which
	// says more than "unknown error" does.
	Desc string
}

UVError is one failure in the shape a Node program reads it: the code it branches on, the number err.errno carries, and the description that goes in the message. Filesystem and socket errors are the same shape because they are the same vocabulary, libuv's, so one type answers for both.

Why this is not a raw errno comparison

Node's filesystem errors are libuv's, and libuv has one set of codes on every platform. Underneath, an error on Windows is a Win32 error number that has nothing to do with the POSIX one: a missing file is ERROR_FILE_NOT_FOUND, which is 2, and so is POSIX ENOENT, but a directory that is not empty is ERROR_DIR_NOT_EMPTY at 145 where POSIX ENOTEMPTY is 39, and access denied is ERROR_ACCESS_DENIED at 5 where POSIX EACCES is 13. Comparing the number an error carries against syscall.ENOENT and friends therefore answers by accident on Windows: right for the two numbers that happen to coincide, wrong or UNKNOWN for the rest. A program that branches on err.code === "ENOTEMPTY" got UNKNOWN.

So the translation is per platform, the way libuv's own is: uvCode lives in a pair of build-tagged files, one reading POSIX errnos and one reading Win32 error numbers, and both answer in libuv's vocabulary. The tables and the descriptions come from libuv itself, include/uv/errno.h and src/win/error.c, so bento reports what Node reports rather than an approximation of it.

Why errno is not just the code's POSIX number

Because err.errno is libuv's number, not the platform's. On every platform but Windows that is the negated POSIX errno (UV__ERR(x) is -x), so ENOENT is -2. On Windows libuv does not trust the C runtime's errno values at all and assigns its own block, where ENOENT is -4058. Both are in the platform files next to the code translation they belong with.

func ClassifyFSError

func ClassifyFSError(err error) UVError

ClassifyFSError maps a Go filesystem error to the Node error it stands for.

The platform's errno is asked first and the standard library's sentinels are the fallback, which is the opposite of the obvious order and is deliberate. The errno is the precise answer: fs.ErrPermission matches both EACCES and EPERM, which are one sentinel in Go and two different codes in Node, and there is no sentinel at all for ENOTEMPTY, EISDIR or ENOTDIR. The sentinels still matter, because an error that never came from a syscall carries no errno: one an io/fs implementation returned, or a bare os.ErrNotExist a helper wrapped.

func ClassifySocketError

func ClassifySocketError(err error) UVError

ClassifySocketError maps a Go network error to the Node error it stands for.

It is ClassifyFSError's sibling and works the same way, by asking the errno rather than reading the message. Reading the message is what bento used to do here, matching on "address already in use" and "permission denied", and that is wrong twice over on Windows: the text comes from the Winsock error, which says "Only one usage of each socket address is normally permitted", so nothing matched and every socket error arrived with no code at all.

The errno on Windows is a Winsock number rather than a POSIX one, WSAECONNREFUSED being 10061 where POSIX ECONNREFUSED is 111, which is the other half of the same problem and is handled where the codes are translated, in the platform files.

Name resolution is asked before the errno rather than after it, which is the reverse of ClassifyFSError and matters on Windows. A failed lookup there does carry an errno, WSAHOST_NOT_FOUND, and libuv translates that to ENOENT, so asking the number first would answer ENOENT where Node says ENOTFOUND. A *net.DNSError is unambiguous about what failed, so it decides on its own.

A deadline is the other failure with no errno behind it: Go reports a dial timeout through os.ErrDeadlineExceeded and Node calls it ETIMEDOUT.

type UserInfo

type UserInfo struct {
	Username string `json:"username"`
	UID      int    `json:"uid"`
	GID      int    `json:"gid"`
	Shell    string `json:"shell"`
	Homedir  string `json:"homedir"`
}

UserInfo is what os.userInfo() answers about the user this process runs as. The two Windows fields have no meaning there and Node reports minus one for them, which is what the platform's own lookup answers.

func OSUserInfo

func OSUserInfo() UserInfo

OSUserInfo is os.userInfo(), the user this process runs as.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL