Compare commits

...

16 Commits

Author SHA1 Message Date
Rémy F.
39828c7847
Merge 9b4e07827474ac3a449271e90e0a04fbf9b5c5da into 2d99c323a27ca9013230cfd6fb56ab37f2a0f1d4 2025-09-21 00:57:16 +08:00
Fabrice Bellard
2d99c323a2 Iterator functions:
- removed memory leak
- don't close iterator when IteratorStepValue fails
- fixed  Iterator Wrap next() and return() arguments
- fixed Iterator.from()
2025-09-20 18:30:02 +02:00
Fabrice Bellard
cf0e179263 Iterator is an abstract class (bnoordhuis) 2025-09-20 17:05:13 +02:00
Fabrice Bellard
3dcca0d1d6 fix Iterator.prototype.constructor (initial patch by bnoordhuis) 2025-09-20 17:01:52 +02:00
Fabrice Bellard
b2ed2e91f5 added Iterator.prototype.[drop,filter,flatMap,map,take,every,find,forEach,some,reduce,[Symbol.toStringTag]] (saghul) 2025-09-20 16:47:43 +02:00
Fabrice Bellard
e924173c0f added Iterator.prototype.toArray and Iterator.from (bnoordhuis) 2025-09-20 16:27:45 +02:00
Fabrice Bellard
982b7aa14f added the Iterator object 2025-09-20 16:05:36 +02:00
Fabrice Bellard
0377dab4f2 removed uninitialized values - removed useless init 2025-09-20 14:31:14 +02:00
Fabrice Bellard
0cef7f0ddb set methods: removed memory leaks - fixed ordering of property access - fixed conversion to integer of 'size' in GetSetRecord() - added missing iterator close - factorized code 2025-09-20 14:21:39 +02:00
Fabrice Bellard
a1e073e444 added set methods (bnoordhuis) 2025-09-20 12:19:50 +02:00
Fabrice Bellard
de4d3927b8 removed memory leak (#441) 2025-09-18 10:42:13 +02:00
Fabrice Bellard
dc7af0ac42 updated release.sh 2025-09-18 10:28:33 +02:00
Rémy F
9b4e078274
Use hint as getaddrinfo 2nd arg 2025-06-29 12:40:10 +00:00
Rémy F
ce1c90dfbe
Add types 2025-06-29 12:38:47 +00:00
Rémy F
e2e64a6ec5
windows support 2025-06-23 22:33:47 +02:00
Rémy F
29a8f53a05
Add Async Socket API 2025-06-14 19:08:31 +00:00
13 changed files with 2844 additions and 77 deletions

View File

@ -247,6 +247,8 @@ HOST_LIBS=-lm -ldl -lpthread
LIBS=-lm -lpthread LIBS=-lm -lpthread
ifndef CONFIG_WIN32 ifndef CONFIG_WIN32
LIBS+=-ldl LIBS+=-ldl
else
LIBS+=-lws2_32
endif endif
LIBS+=$(EXTRA_LIBS) LIBS+=$(EXTRA_LIBS)

2
TODO
View File

@ -62,5 +62,5 @@ Optimization ideas:
Test262o: 0/11262 errors, 463 excluded Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Result: 54/79414 errors, 1637 excluded, 6821 skipped Result: 54/80760 errors, 1631 excluded, 6064 skipped
Test262 commit: e7e136756cd67c1ffcf7c09d03aeb8ad5a6cec0c Test262 commit: e7e136756cd67c1ffcf7c09d03aeb8ad5a6cec0c

14
doc/globals.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
/** Command line arguments where argument[0] is the JS script name. */
declare const scriptArgs: string[];
/** Print args separated by spaces and a trailing newline. */
declare function print(...args: any[]): void;
declare const console: {
/** Print args separated by spaces and a trailing newline. */
log: typeof print
};
interface ImportMeta {
url: string;
main: boolean;
}

290
doc/os.d.ts vendored Normal file
View File

@ -0,0 +1,290 @@
/**
* Provides Operating System specific functions:
* file access, sockets, signals, timers, async I/O, workers (threads)
*/
declare module "os" {
type Success = 0;
type Errno = number;
type NegativeErrno = number;
type Result<T> = T | NegativeErrno;
type ExitStatus = number;
type WaitStatus = number;
type Branded<T, B> = T & { __brand: B }; // Prevent interchangeable primitives (e.g. number)
type OpenOption = Branded<number, "OpenOption">;
type SocketFamily = Branded<number, "SocketFamily">;
type SocketType = Branded<number, "SocketType">;
type SocketOpt = Branded<number, "SocketOpt">;
type SocketShutOpt = Branded<number, "SocketShutOpt">;
type PollOpt = Branded<number, "PollOpt">;
type PollRet = Branded<number, "PollRet">;
type WaitpidOpt = Branded<number, "WaitpidOpt">;
type FileDescriptor = Branded<number, "FileDescriptor">;
type Signal = Branded<number, "Signal">;
type StatMode = Branded<number, "StatMode">;
type Pid = Branded<number, "Pid">;
type TimerHandle = Branded<unknown, "TimerHandle">;
type Callback = () => void;
type Platform = "linux" | "darwin" | "win32" | "js";
type WorkerMessage = any;
interface ExecOptions {
/**
* Boolean (default = `true`). If `true`, wait until the process is
* terminated. In this case, exec return the exit code if positive or the
* negated signal number if the process was interrupted by a signal. If
* false, do not block and return the process id of the child.
*/
block?: boolean;
/** Is the file searched in the PATH @default true. */
usePath?: boolean;
/** Set the file to be executed @default args[0] */
file?: string,
/** The working directory of the new process */
cwd?: string,
/** Handler for stdin */
stdin?: FileDescriptor,
/** Handler for stdout */
stdout?: FileDescriptor,
/** Handler for stderr */
stderr?: FileDescriptor,
/** set the process environment from the object key-value pairs.
* If unset, it will inherit them from current process. */
env?: { [key: string]: string },
/** Process uid with `setuid` */
uid?: number,
/** Process gid with `setgid` */
gid?: number,
}
type ExecNonBlockingOptions = ExecOptions & { block?: false };
type ExecBlockingOptions = ExecOptions & { block: true };
interface Stat {
dev: number;
ino: number;
mode: StatMode;
nlink: number;
uid: number;
gid: number;
rdev: number;
size: number;
blocks: number;
/** milliseconds since 1970 */
atime: number;
/** milliseconds since 1970 */
mtime: number;
/** milliseconds since 1970 */
ctime: number;
}
interface SocketAddr {
/** @example 80 */
port: number;
/** @example "8.8.8.8" or "::1" */
addr: string;
/** @default AF_INET */
family: SocketFamily;
}
interface SocketAddrInfo extends SocketAddr {
socktype: SocketType;
}
interface HintAddr {
/** @example 80 or "http" */
service?: number | string;
/** @default any */
family?: SocketFamily;
/** @default any */
socktype?: SocketType;
}
/* POSIX constants (Windows-specific) */
export const O_TEXT: OpenOption;
/* POSIX constants */
export const O_APPEND: OpenOption;
export const O_CREAT: OpenOption;
export const O_EXCL: OpenOption;
export const O_RDONLY: OpenOption;
export const O_RDWR: OpenOption;
export const O_TRUNC: OpenOption;
export const O_WRONLY: OpenOption;
export const S_IFBLK: StatMode;
export const S_IFCHR: StatMode;
export const S_IFDIR: StatMode;
export const S_IFIFO: StatMode;
export const S_IFLNK: StatMode;
export const S_IFMT: StatMode;
export const S_IFREG: StatMode;
export const S_IFSOCK: StatMode;
export const S_ISGID: StatMode;
export const S_ISUID: StatMode;
export const SIGABRT: Signal;
export const SIGALRM: Signal;
export const SIGCHLD: Signal;
export const SIGCONT: Signal;
export const SIGFPE: Signal;
export const SIGILL: Signal;
export const SIGINT: Signal;
export const SIGPIPE: Signal;
export const SIGQUIT: Signal;
export const SIGSEGV: Signal;
export const SIGSTOP: Signal;
export const SIGTERM: Signal;
export const SIGTSTP: Signal;
export const SIGTTIN: Signal;
export const SIGTTOU: Signal;
export const SIGUSR1: Signal;
export const SIGUSR2: Signal;
export const WNOHANG: WaitpidOpt;
export const AF_INET: SocketFamily;
export const AF_INET6: SocketFamily;
export const SOCK_STREAM: SocketType;
export const SOCK_DGRAM: SocketType;
export const SOCK_RAW: SocketType;
//export const SOCK_BLOCK: SocketType; // SOCK_NONBLOCK
export const SO_REUSEADDR: SocketOpt;
export const SO_ERROR: SocketOpt;
export const SO_RCVBUF: SocketOpt;
export const SHUT_RD: SocketShutOpt;
export const SHUT_WR: SocketShutOpt;
export const SHUT_RDWR: SocketShutOpt;
/** string representing the platform. */
export const platform: Platform;
/** Open a file. Return a handle or `< 0` if error. */
export function open(filename: string, flags?: OpenOption, mode?: number): Result<FileDescriptor>;
/** Close the file handle `fd`. */
export function close(fd: FileDescriptor): Result<Success>;
/** Seek in the file. Use `std.SEEK_*` for whence */
export function seek(fd: FileDescriptor, offset: number, whence: number): Result<number>;
export function seek(fd: FileDescriptor, offset: bigint, whence: number): Result<bigint>;
/** Read `length` bytes from the file handle `fd` to the `ArrayBuffer` buffer at byte position `offset` */
export function read(fd: FileDescriptor, buffer: ArrayBuffer, offset: number, length: number): Result<number>;
/** Write `length` bytes to the file handle `fd` from the ArrayBuffer `buffer` at byte position `offset` */
export function write(fd: FileDescriptor, buffer: ArrayBuffer, offset: number, length: number): Result<number>;
/** Return `true` is fd is a TTY (terminal) handle. */
export function isatty(fd: FileDescriptor): boolean;
/** Return the TTY size as `[width, height]` or `null` if not available. */
export function ttyGetWinSize(fd: FileDescriptor): [width: number, height: number] | null;
/** Set the TTY in raw mode. */
export function ttySetRaw(fd: FileDescriptor): void;
/** Remove a file. */
export function remove(filename: string): Result<Success>;
/** Rename a file. */
export function rename(filename: string): Result<Success>;
/** Get the canonicalized absolute pathname of `path` */
export function realpath(path: string): [absPath: string, code: Success | Errno];
/** Return the current working directory */
export function getcwd(): [cwd: string, code: Success | Errno];
/** Change the current directory. Return 0 if OK or `-errno`. */
export function chdir(): Result<Success>;
/** Create a directory at `path`. Return 0 if OK or `-errno`. */
export function mkdir(path: string, mode?: number): Result<Success>;
/** Get a file status */
export function stat(path: string): [fileStatus: Stat, code: Success | Errno]
/** Get a link status */
export function lstat(path: string): [linkStatus: Stat, code: Success | Errno]
/** Change the access and modification times of the file `path` @returns ms since 1970. */
export function utimes(path: string, atime: number, mtime: number): Result<Success>;
/** Create a link at `linkpath` containing the string `target`. */
export function symlink(target: string, linkpath: string): Result<Success>;
/** Get link target */
export function readlink(path: string): [linkTarget: string, code: Success | Errno];
/** List directory entries */
export function readdir(dirPath: string): [dirFilenames: string[], code: Success | Errno];
/** Set the single `func` read handler to be called each time data can be written to `fd`. */
export function setReadHandler(fd: FileDescriptor, func: Callback): void;
/** Remove the read handler for `fd`. */
export function setReadHandler(fd: FileDescriptor, func: null): void;
/** Set the single `func` read handler to be called each time data can be written to `fd`. */
export function setWriteHandler(fd: FileDescriptor, func: Callback): void;
/** Remove the write handler for `fd`. */
export function setWriteHandler(fd: FileDescriptor, func: null): void;
/** Set the single `func` to be called when `signal` happens. Work in main thread only */
export function signal(signal: Signal, func: Callback): void
/** Call the default handler when `signal` happens. */
export function signal(signal: Signal, func: null): void
/** Ignore when `signal` happens. */
export function signal(signal: Signal, func: undefined): void
/** Send the signal `sig` to the process `pid`. */
export function kill(pid: Pid, signal: Signal): Result<number>;
/** Execute a process with the arguments args. */
export function exec(args: string[], options?: ExecBlockingOptions): Result<ExitStatus>;
/** Execute a process with the arguments args. */
export function exec(args: string[], options: ExecNonBlockingOptions): Result<Pid>;
/** `waitpid` Unix system call. */
export function waitpid(pid: Pid, options: WaitpidOpt): [ret: Result<Pid | Success>, status: WaitStatus];
/** `getpid` Unix system call. */
export function getpid(): [Pid];
/** `dup` Unix system call. */
export function dup(fd: FileDescriptor): Result<FileDescriptor>;
/** `dup2` Unix system call. */
export function dup2(oldFd: FileDescriptor, newFd: FileDescriptor): Result<FileDescriptor>;
/** `pipe` Unix system call. */
export function pipe(): [readFd: FileDescriptor, writeFd: FileDescriptor] | null;
/** Sleep during `delay_ms` milliseconds. */
export function sleep(delay_ms: number): Result<number>;
/** Sleep during `delay_ms` milliseconds. */
export function sleepAsync(delay_ms: number): Promise<Result<number>>;
/** Call the function func after `delay` ms. */
export function setTimeout(func: Callback, delay: number): TimerHandle;
/** Cancel a timer. */
export function clearTimeout(handle: TimerHandle): void;
/** Create a POSIX socket */
export function socket(family: SocketFamily, type: SocketType): Result<FileDescriptor>;
/** Get a socket option @example os.getsockopt(sock_srv, os.SO_RCVBUF, uintArr1.buffer); */
export function getsockopt(sockfd: FileDescriptor, name: SocketOpt, data: ArrayBuffer): Result<Success>;
/** Set a socket option @example os.setsockopt(sock_srv, os.SO_REUSEADDR, new Uint32Array([1]).buffer); */
export function setsockopt(sockfd: FileDescriptor, name: SocketOpt, data: ArrayBuffer): Result<Success>;
/** Get address information for a given node and/or service @example os.getaddrinfo("localhost", {family:os.AF_INET6}) */
export function getaddrinfo(node?: string, hint?: HintAddr): Result<Array<SocketAddrInfo>>;
/** Get current address to which the socket sockfd is bound */
export function getsockname(sockfd: FileDescriptor): Result<SocketAddr>;
/** Bind socket to a specific address */
export function bind(sockfd: FileDescriptor, addr: SocketAddr): Result<Success>;
/** Mark `sockfd` as passive socket that will `accept()` a `backlog` number of incoming connection (SOMAXCONN by default). */
export function listen(sockfd: FileDescriptor, backlog?: number): Result<Success>;
/** Shut down part of a full-duplex connection */
export function shutdown(sockfd: FileDescriptor, type: SocketShutOpt): Result<Success>;
/** Accept incoming connections */
export function accept(sockfd: FileDescriptor): Promise<[remotefd: FileDescriptor, remoteaddr: SocketAddr]>;
/** Connect `sockfd` to `addr` */
export function connect(sockfd: FileDescriptor, addr: SocketAddr): Promise<Result<Success>>;
/** Send `length` byte from `buffer` on `sockfd` @returns bytes sent or <0 if error */
export function send(sockfd: FileDescriptor, buffer: ArrayBuffer, length?: number): Promise<Result<number>>;
/** Receive `length` byte in `buffer` from `sockfd` @returns bytes received or <0 if error */
export function recv(sockfd: FileDescriptor, buffer: ArrayBuffer, length?: number): Promise<Result<number>>;
/** Send `length` byte from `buffer` on `sockfd` to `addr` @returns bytes sent or <0 if error */
export function sendto(sockfd: FileDescriptor, addr: SocketAddr, buffer: ArrayBuffer, length?: number): Promise<Result<number>>;
/** Receive `length` byte in `buffer` from `sockfd` @returns bytes received or <0 if error, and remote address used */
export function recvfrom(sockfd: FileDescriptor, buffer: ArrayBuffer, length?: number): Promise<[total: Result<number>, from: SocketAddr]>;
export class Worker {
/**
* In the created worker, `Worker.parent` represents the parent worker
* and is used to send or receive messages.
*/
static parent?: Worker;
/**
* Constructor to create a new thread (worker) with an API close to
* the `WebWorkers`. `module_filename` is a string specifying the
* module filename which is executed in the newly created thread. As
* for dynamically imported module, it is relative to the current
* script or module path. Threads normally dont share any data and
* communicate between each other with messages. Nested workers are
* not supported. An example is available in `tests/test_worker.js`.
*/
constructor(module_filename: string);
/**
* Send a message to the corresponding worker. msg is cloned in the
* destination worker using an algorithm similar to the HTML structured
* clone algorithm. SharedArrayBuffer are shared between workers.
*
* Current limitations: `Map` and `Set` are not supported yet.
*/
postMessage(msg: WorkerMessage): void;
/**
* Getter and setter. Set a function which is called each time a
* message is received. The function is called with a single argument.
* It is an object with a `data` property containing the received
* message. The thread is not terminated if there is at least one non
* `null` onmessage handler.
*/
onmessage: (msg: WorkerMessage) => void;
}
}

279
doc/std.d.ts vendored Normal file
View File

@ -0,0 +1,279 @@
/**
* The std module provides wrappers to the libc stdlib.h and stdio.h and a
* few other utilities.
*/
declare module "std" {
import { FileDescriptor, ExitStatus, Errno } from "os";
/**
* FILE prototype
*/
export interface FILE {
/**
* Close the file. Return 0 if OK or `-errno` in case of I/O error.
*/
close(): number
/**
* Outputs the string with the UTF-8 encoding.
*/
puts(str: string): void;
/**
* Formatted printf.
*
* The same formats as the standard C library printf are supported.
* Integer format types (e.g. `%d`) truncate the Numbers or BigInts to 32
* bits. Use the `l` modifier (e.g. `%ld`) to truncate to 64 bits.
*/
printf(format: string, ...args: any[]): number;
/**
* Flush the buffered file.
*/
flush(): void;
/**
* Seek to a give file position (whence is `std.SEEK_*`). `offset` can
* be a number or a bigint. Return 0 if OK or `-errno` in case of I/O
* error.
*/
seek(offset: number, whence: number): number;
/**
* Return the current file position.
*/
tell(): number;
/**
* Return the current file position as a bigint.
*/
tello(): bigint;
/**
* Return true if end of file.
*/
eof(): boolean;
/**
* Return the associated OS handle.
*/
fileno(): FileDescriptor;
/**
* Return true if there was an error.
*/
error(): boolean;
/**
* Clear the error indication.
*/
clearerr(): void;
/**
* Read `length` bytes from the file to the ArrayBuffer `buffer` at
* byte position `position` (wrapper to the libc `fread`).
*/
read(buffer: ArrayBuffer, position: number, length: number): number;
/**
* Write `length` bytes to the file from the ArrayBuffer `buffer` at
* byte position position (wrapper to the libc `fwrite`).
*/
write(buffer: ArrayBuffer, postion: number, length: number): number;
/**
* Return the next line from the file, assuming UTF-8 encoding, excluding
* the trailing line feed.
*/
getline(): string;
/**
* Read `max_size` bytes from the file and return them as a string
* assuming UTF-8 encoding. If `max_size` is not present, the file is
* read up its end.
*/
readAsString(max_size?: number): string;
/**
* Return the next byte from the file. Return -1 if the end of file is
* reached.
*/
getByte(): number
/**
* Write one byte to the file.
*/
putByte(c: number): number;
}
export interface EvalOptions {
/**
* Boolean (default = `false`). If `true`, error backtraces do not list
* the stack frames below the evalScript.
*/
backtrace_barrier?: boolean;
}
export interface ErrorObj {
errno?: number;
}
export interface UrlGetOptions {
/**
* Boolean (default = `false`). If `true`, the response is an
* ArrayBuffer instead of a string. When a string is returned, the
* data is assumed to be UTF-8 encoded.
*/
binary?: boolean;
/**
* Boolean (default = `false`). If `true`, return the an object contains
* the properties response (response content), responseHeaders (headers
* separated by CRLF), status (status code). response is null is case of
* protocol or network error. If full is false, only the response is
* returned if the status is between 200 and 299. Otherwise null is
* returned.
*/
full?: boolean;
}
export interface UrlGetResponse<T> {
response: T | null;
status: number;
responseHeaders: string;
}
/**
* Result that either represents a FILE or null on error.
*/
export type FILEResult = FILE | null;
/**
* Exit the process.
*/
export function exit(n: ExitStatus): never;
/**
* Evaluate the string `str` as a script (global eval).
*/
export function evalScript(str: string, options?: EvalOptions): any;
/**
* Evaluate the file filename as a script (global eval).
*/
export function loadScript(filename: string): any;
/**
* Load the file filename and return it as a string assuming UTF-8
* encoding. Return `null` in case of I/O error.
*/
export function loadFile(filename: string): string | null;
/**
* Open a file (wrapper to the libc fopen()). Return the FILE object or
* `null` in case of I/O error. If errorObj is not undefined, set its
* `errno` property to the error code or to 0 if no error occured.
*/
export function open(filename: string, flags: string, errorObj?: ErrorObj): FILEResult;
/**
* Open a process by creating a pipe (wrapper to the libc `popen()`).
* Return the `FILE` object or `null` in case of I/O error. If `errorObj`
* is not `undefined`, set its `errno` property to the error code or to 0
* if no error occured.
*/
export function popen(command: string, flags: string, errorObj?: ErrorObj): FILEResult;
/**
* Open a file from a file handle (wrapper to the libc `fdopen()`). Return
* the `FILE` object or `null` in case of I/O error. If `errorObj` is not
* `undefined`, set its errno property to the error code or to 0 if no
* error occured.
*/
export function fdopen(fd: FileDescriptor, flags: string, errorObj?: ErrorObj): FILEResult;
/**
* Open a temporary file. Return the `FILE` object or `null` in case of I/O
* error. If `errorObj` is not undefined, set its `errno` property to the
* error code or to 0 if no error occured.
*/
export function tmpfile(errorObj?: ErrorObj): FILE;
/**
* Equivalent to `std.out.puts(str)`.
*/
export const puts: typeof out.puts;
/**
* Equivalent to `std.out.printf(fmt, ...args)`.
*/
export const printf: typeof out.printf;
/**
* Equivalent to the libc `sprintf()`.
*/
export function sprintf(format: string, ...args: any[]): string;
const $in: FILE;
/**
* Wrappers to the libc file `stdin`, `stdout`, `stderr`.
*/
export { $in as in };
/**
* Wrappers to the libc file `stdin`, `stdout`, `stderr`.
*/
export const out: FILE;
/**
* Wrappers to the libc file `stdin`, `stdout`, `stderr`.
*/
export const err: FILE;
/**
* Constants for seek().
*/
export const SEEK_CUR: number;
/**
* Constants for seek().
*/
export const SEEK_END: number;
/**
* Constants for seek().
*/
export const SEEK_SET: number;
/**
* Enumeration object containing the integer value of common errors
* (additional error codes may be defined):
*/
export const Error: {
readonly EACCES: number,
readonly ENOENT: number,
readonly EBADF: number,
readonly ENOSPC: number,
readonly EBUSY: number,
readonly ENOSYS: number,
readonly EEXIST: number,
readonly EPERM: number,
readonly EINVAL: number,
readonly EPIPE: number,
readonly EIO: number,
readonly EAGAIN: number,
readonly EINPROGRESS: number,
readonly EWOULDBLOCK: number,
};
/**
* Return a string that describes the error `errno`.
*/
export function strerror(errno: Errno): string;
/**
* Manually invoke the cycle removal algorithm. The cycle removal
* algorithm is automatically started when needed, so this function is
* useful in case of specific memory constraints or for testing.
*/
export function gc(): void;
/**
* Return the value of the environment variable `name` or `undefined` if it
* is not defined.
*/
export function getenv(name: string): string | undefined;
/**
* Set the value of the environment variable `name` to the string `value`.
*/
export function setenv(name: string, value: string): void;
/**
* Delete the environment variable `name`.
*/
export function unsetenv(name: string): void;
/**
* Return an object containing the environment variables as key-value pairs.
*/
export function getenviron(): { [key: string]: string };
/** Download url using the curl command line utility. */
export function urlGet(url: string, options?: UrlGetOptions): string | null;
export function urlGet(url: string, options: UrlGetOptions & { full?: false, binary: true }): ArrayBuffer | null;
export function urlGet(url: string, options: UrlGetOptions & { full: true, binary?: false }): UrlGetResponse<string>;
export function urlGet(url: string, options: UrlGetOptions & { full: true, binary: true }): UrlGetResponse<ArrayBuffer>;
/**
* Parse `str` using a superset of `JSON.parse`. The following extensions are accepted:
*
* - Single line and multiline comments
* - unquoted properties (ASCII-only Javascript identifiers)
* - trailing comma in array and object definitions
* - single quoted strings
* - \\f and \\v are accepted as space characters
* - leading plus in numbers
* - octal (0o prefix) and hexadecimal (0x prefix) numbers
*/
export function parseExtJSON(str: string): any;
}

25
examples/http_client.js Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env qjs
///@ts-check
/// <reference path="../doc/globals.d.ts" />
/// <reference path="../doc/os.d.ts" />
/// <reference path="../doc/std.d.ts" />
import * as os from "os";
import * as std from "std";
/** @template T @param {os.Result<T>} result */
function must(result) {
if (typeof result === "number" && result < 0) throw new Error(std.strerror(-result));
return /** @type {T} */ (result)
}
//USAGE: client.js wttr.in/paris
const uriRegexp = /^(?<host>[A-Za-z0-9\-\.]+)(?<port>:[0-9]+)?(?<query>.*)$/;
const { host = "bellard.org", port = ":80", query = "/" } = scriptArgs[1]?.match(uriRegexp)?.groups || {};
console.log("sending GET on",{ host, port, query })
const [addr] = must(os.getaddrinfo(host, { service: port.slice(1) }));
const sockfd = must(os.socket(addr.family, addr.socktype));
await os.connect(sockfd, addr);
const httpReq = Uint8Array.from(`GET ${query||'/'} HTTP/1.0\r\nHost: ${host}\r\nUser-Agent: curl\r\n\r\n`, c => c.charCodeAt(0))
must(await os.send(sockfd, httpReq.buffer) > 0);
const chunk = new Uint8Array(512);
const recvd = await os.recv(sockfd, chunk.buffer);
console.log([...chunk.slice(0, recvd)].map(c => String.fromCharCode(c)).join(''));

96
examples/http_server.js Executable file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env qjs
///@ts-check
/// <reference path="../doc/globals.d.ts" />
/// <reference path="../doc/os.d.ts" />
/// <reference path="../doc/std.d.ts" />
import * as os from "os";
import * as std from "std";// for std.strerror
const MIMES = new Map([
['html', 'text/html'],
['txt', 'text/plain'],
['css', 'text/css'],
['c', 'text/plain'],
['h', 'text/plain'],
['json', 'application/json'],
['mjs', 'application/javascript'],
['js', 'application/javascript'],
['', 'application/octet-stream'],
]);
/** @template T @param {os.Result<T>} result */
function must(result) {
if (typeof result === "number" && result < 0) throw new Error(std.strerror(-result));
return /** @type {T} */ (result)
}
/**@param {os.FileDescriptor} fd */
async function* recvLines(fd) {
const chunk = new Uint8Array(1);
let line = '';
while (await os.recv(fd, chunk.buffer) > 0) {
const char = String.fromCharCode(...chunk);
if (char == '\n') {
yield line;
line = '';
} else line += char;
}
if (line) yield line;
}
/** @param {os.FileDescriptor} fd @param {string[]} lines */
function sendLines(fd, lines) {
const buf = Uint8Array.from(lines.join('\r\n'), c => c.charCodeAt(0));
return os.send(fd, buf.buffer);
}
//USAGE: qjs http_server.js [PORT=8080 [HOST=localhost]]
const [port = "8080", host = "localhost"] = scriptArgs.slice(1);
const [ai] = must(os.getaddrinfo(host, { service: port }));
//if (!ai.length) throw `Unable to getaddrinfo(${host}, ${port})`;
const sock_srv = must(os.socket(os.AF_INET, os.SOCK_STREAM));
must(os.setsockopt(sock_srv, os.SO_REUSEADDR, new Uint32Array([1]).buffer));
must(os.bind(sock_srv, ai));
must(os.listen(sock_srv));
//os.signal(os.SIGINT, ()=>os.close(sock_srv)); // don't work
console.log(`Listening on http://${host}:${port} (${ai.addr}:${ai.port}) ...`);
const openCmd = { linux: "xdg-open", darwin: "open", win32: "start" }[os.platform];
if (openCmd && os.exec) os.exec([openCmd, `http://${host}:${port}`]);
while (true) { // TODO: break on SIG*
const [sock_cli] = await os.accept(sock_srv);
const lines = recvLines(sock_cli);
const [method, path, http_ver] = ((await lines.next()).value || '').split(' ');
let safe_path = '.' + path.replaceAll(/\.+/g, '.'); // may += index.html later
console.log(method, safe_path, http_ver);
const headers = new Map()
for await (const line of lines) {
const header = line.trimEnd();
if (!header) break;
const sepIdx = header.indexOf(': ');
headers.set(header.slice(0, sepIdx), header.slice(sepIdx + 2));
}
let [obj, err] = os.stat(safe_path);
if (obj?.mode & os.S_IFDIR && safe_path.endsWith('/') && os.stat(safe_path + 'index.html')[0]) {
safe_path += 'index.html';
[obj, err] = os.stat(safe_path);
}
if (err) {
await sendLines(sock_cli, ['HTTP/1.1 404', '', safe_path, 'errno:' + err])
} else if (obj?.mode & os.S_IFDIR) {
if (!safe_path.endsWith('/'))
await sendLines(sock_cli, ['HTTP/1.1 301', `Location: ${safe_path}/`, '']);
else
await sendLines(sock_cli, ['HTTP/1.1 200', 'Content-Type: text/html', '',
os.readdir(safe_path)[0]?.filter(e => e[0] != '.').map(e => `<li><a href="${e}">${e}</a></li>`).join('')
]);
} else {
const mime = MIMES.get(safe_path.split('.').at(-1) || '') || MIMES.get('');
await sendLines(sock_cli, ['HTTP/1.1 200', `Content-Type: ${mime}`, '', '']);
const fd = must(os.open(safe_path));
const fbuf = new Uint8Array(4096);
for (let got = 0; (got = os.read(fd, fbuf.buffer, 0, fbuf.byteLength)) > 0;) {
await os.send(sock_cli, fbuf.buffer, got);
}
}
os.close(sock_cli);
}

5
qjs.c
View File

@ -39,6 +39,11 @@
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
#include <malloc_np.h> #include <malloc_np.h>
#endif #endif
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#endif
#include "cutils.h" #include "cutils.h"
#include "quickjs-libc.h" #include "quickjs-libc.h"

View File

@ -78,6 +78,8 @@ DEF(await, "await")
/* empty string */ /* empty string */
DEF(empty_string, "") DEF(empty_string, "")
/* identifiers */ /* identifiers */
DEF(keys, "keys")
DEF(size, "size")
DEF(length, "length") DEF(length, "length")
DEF(fileName, "fileName") DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber") DEF(lineNumber, "lineNumber")
@ -228,6 +230,9 @@ DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */ DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */ DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */ DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Iterator, "Iterator")
DEF(IteratorHelper, "Iterator Helper")
DEF(IteratorWrap, "Iterator Wrap")
DEF(Map_Iterator, "Map Iterator") DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator") DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator") DEF(Array_Iterator, "Array Iterator")

View File

@ -38,6 +38,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h> #include <windows.h>
#include <conio.h> #include <conio.h>
#include <utime.h> #include <utime.h>
@ -46,6 +48,12 @@
#include <termios.h> #include <termios.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
extern char **environ; extern char **environ;
@ -80,16 +88,37 @@ typedef sig_t sighandler_t;
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
/* TODO: typedef enum {
- add socket calls MAGIC_SOCKET_RECV,
*/ MAGIC_SOCKET_SEND,
MAGIC_SOCKET_RECVFROM,
MAGIC_SOCKET_SENDTO,
MAGIC_SOCKET_CONNECT,
MAGIC_SOCKET_ACCEPT,
} MagicSocket;
typedef struct { typedef struct {
struct list_head link; struct list_head link;
int fd; int fd;
JSValue rw_func[2]; JSValue r_func;
JSValue w_func;
} JSOSRWHandler; } JSOSRWHandler;
typedef struct {
struct list_head link;
int sockfd;
MagicSocket magic;
uint64_t length;
uint8_t* buffer;
JSValue bufval;
struct sockaddr_storage sockaddr; // for sendto()
JSValue resolve;
JSValue reject;
#ifdef _WIN32
WSAEVENT event; // so os_pool can wait on it
#endif
} JSOSSockHandler;
typedef struct { typedef struct {
struct list_head link; struct list_head link;
int sig_num; int sig_num;
@ -144,6 +173,7 @@ typedef struct {
typedef struct JSThreadState { typedef struct JSThreadState {
struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */
struct list_head os_sock_handlers; /* list of JSOSSockHandler.link */
struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */
struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head os_timers; /* list of JSOSTimer.link */
struct list_head port_list; /* list of JSWorkerMessageHandler.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */
@ -167,6 +197,93 @@ static BOOL my_isdigit(int c)
return (c >= '0' && c <= '9'); return (c >= '0' && c <= '9');
} }
static int JS_ToSockaddrStruct(JSContext *ctx, JSValue addr,
struct sockaddr_storage *sockaddr, uint32_t sockfd)
{
JSValue val;
const char *addr_str;
uint32_t port, family;
int ret;
val = JS_GetPropertyStr(ctx, addr, "family");
if (JS_IsException(val))
return -1;
if (JS_IsUndefined(val)) { // get from sockfd when no .family given
struct sockaddr_storage saddr;
socklen_t len = sizeof(saddr);
if (getsockname(sockfd, (struct sockaddr *)&saddr, &len) != -1) {
family = saddr.ss_family;
} else {
family = AF_INET;
}
} else if (JS_ToUint32(ctx, &family, val))
return -1;
sockaddr->ss_family = family;
JS_FreeValue(ctx, val);
val = JS_GetPropertyStr(ctx, addr, "addr");
if (JS_IsException(val))
return -1;
addr_str = JS_ToCString(ctx, val);
if (!addr_str)
return -1;
void* sin_addr = family == AF_INET ?
(void*)&(((struct sockaddr_in *)sockaddr)->sin_addr):
(void*)&(((struct sockaddr_in6 *)sockaddr)->sin6_addr);
ret = inet_pton(sockaddr->ss_family, addr_str, sin_addr);
JS_FreeCString(ctx, addr_str);
JS_FreeValue(ctx, val);
if (ret != 1)
return -1;
val = JS_GetPropertyStr(ctx, addr, "port");
ret = JS_ToUint32(ctx, &port, val);
JS_FreeValue(ctx, val);
if (ret)
return -1;
if (family == AF_INET)
((struct sockaddr_in *)sockaddr)->sin_port = htons(port);
if (family == AF_INET6)
((struct sockaddr_in6 *)sockaddr)->sin6_port = htons(port);
return 0;
}
static JSValue JS_ToSockaddrObj(JSContext *ctx, struct sockaddr_storage *sockaddr)
{
JSValue obj, prop;
char ip_str[INET6_ADDRSTRLEN]; // max(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)
const char *ip_ptr;
struct sockaddr_in *sockaddr4 = (struct sockaddr_in *)sockaddr;
struct sockaddr_in6 *sockaddr6 = (struct sockaddr_in6 *)sockaddr;
obj = JS_NewObject(ctx);
if (JS_IsException(obj))
goto fail;
prop = JS_NewUint32(ctx, sockaddr->ss_family);
JS_DefinePropertyValueStr(ctx, obj, "family", prop, JS_PROP_C_W_E);
uint16_t sin_port = sockaddr->ss_family == AF_INET ? sockaddr4->sin_port :
sockaddr->ss_family == AF_INET6 ? sockaddr6->sin6_port : 0;
prop = JS_NewUint32(ctx, ntohs(sin_port));
JS_DefinePropertyValueStr(ctx, obj, "port", prop, JS_PROP_C_W_E);
void* sin_addr = sockaddr->ss_family == AF_INET ? (void*)&sockaddr4->sin_addr :
sockaddr->ss_family == AF_INET6 ? (void*)&sockaddr6->sin6_addr : NULL;
size_t sin_len = sockaddr->ss_family == AF_INET ? INET_ADDRSTRLEN:
sockaddr->ss_family == AF_INET6 ? INET6_ADDRSTRLEN: 0 ;
ip_ptr = inet_ntop(sockaddr->ss_family, sin_addr, ip_str, sin_len);
prop = ip_ptr ? JS_NewString(ctx, ip_ptr) : JS_NULL;
JS_DefinePropertyValueStr(ctx, obj, "addr", prop, JS_PROP_C_W_E);
return obj;
fail:
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
/* XXX: use 'o' and 'O' for object using JS_PrintValue() ? */ /* XXX: use 'o' and 'O' for object using JS_PrintValue() ? */
static JSValue js_printf_internal(JSContext *ctx, static JSValue js_printf_internal(JSContext *ctx,
int argc, JSValueConst *argv, FILE *fp) int argc, JSValueConst *argv, FILE *fp)
@ -948,6 +1065,18 @@ static ssize_t js_get_errno(ssize_t ret)
return ret; return ret;
} }
static ssize_t js_get_sockerrno(ssize_t ret)
{
#if defined(_WIN32)
if (ret == -1 || ret == INVALID_SOCKET)
ret = -WSAGetLastError();
#else
if (ret == -1)
ret = -errno;
#endif
return ret;
}
static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
@ -1634,6 +1763,9 @@ static const JSCFunctionListEntry js_std_error_props[] = {
DEF(EPERM), DEF(EPERM),
DEF(EPIPE), DEF(EPIPE),
DEF(EBADF), DEF(EBADF),
DEF(EAGAIN),
DEF(EINPROGRESS),
DEF(EWOULDBLOCK),
#undef DEF #undef DEF
}; };
@ -1996,20 +2128,27 @@ static JSOSRWHandler *find_rh(JSThreadState *ts, int fd)
static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh)
{ {
int i;
list_del(&rh->link); list_del(&rh->link);
for(i = 0; i < 2; i++) { JS_FreeValueRT(rt, rh->r_func);
JS_FreeValueRT(rt, rh->rw_func[i]); JS_FreeValueRT(rt, rh->w_func);
}
js_free_rt(rt, rh); js_free_rt(rt, rh);
} }
static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, static void free_sock_handler(JSRuntime *rt, JSOSSockHandler *sh)
{
list_del(&sh->link);
JS_FreeValueRT(rt, sh->resolve);
JS_FreeValueRT(rt, sh->reject);
js_free_rt(rt, sh);
}
static JSValue js_os_setReadWriteHandler(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic) int argc, JSValueConst *argv, int magic)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSRWHandler *rh; JSOSRWHandler *rh;
BOOL isRead = magic == 0;
int fd; int fd;
JSValueConst func; JSValueConst func;
@ -2019,10 +2158,10 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
if (JS_IsNull(func)) { if (JS_IsNull(func)) {
rh = find_rh(ts, fd); rh = find_rh(ts, fd);
if (rh) { if (rh) {
JS_FreeValue(ctx, rh->rw_func[magic]); JSValue* rwfunc = isRead ? &rh->r_func : &rh->w_func;
rh->rw_func[magic] = JS_NULL; JS_FreeValue(ctx, *rwfunc);
if (JS_IsNull(rh->rw_func[0]) && *rwfunc = JS_NULL;
JS_IsNull(rh->rw_func[1])) { if (JS_IsNull(rh->r_func) && JS_IsNull(rh->w_func)) {
/* remove the entry */ /* remove the entry */
free_rw_handler(JS_GetRuntime(ctx), rh); free_rw_handler(JS_GetRuntime(ctx), rh);
} }
@ -2036,12 +2175,13 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
if (!rh) if (!rh)
return JS_EXCEPTION; return JS_EXCEPTION;
rh->fd = fd; rh->fd = fd;
rh->rw_func[0] = JS_NULL; rh->r_func = JS_NULL;
rh->rw_func[1] = JS_NULL; rh->w_func = JS_NULL;
list_add_tail(&rh->link, &ts->os_rw_handlers); list_add_tail(&rh->link, &ts->os_rw_handlers);
} }
JS_FreeValue(ctx, rh->rw_func[magic]); JSValue* rwfunc = isRead ? &rh->r_func : &rh->w_func;
rh->rw_func[magic] = JS_DupValue(ctx, func); JS_FreeValue(ctx, *rwfunc);
*rwfunc = JS_DupValue(ctx, func);
} }
return JS_UNDEFINED; return JS_UNDEFINED;
} }
@ -2347,8 +2487,7 @@ static void js_waker_close(JSWaker *w)
static void js_free_message(JSWorkerMessage *msg); static void js_free_message(JSWorkerMessage *msg);
/* return 1 if a message was handled, 0 if no message */ /* return 1 if a message was handled, 0 if no message */
static int handle_posted_message(JSRuntime *rt, JSContext *ctx, static int handle_posted_message(JSContext *ctx, JSWorkerMessageHandler *port)
JSWorkerMessageHandler *port)
{ {
JSWorkerMessagePipe *ps = port->recv_pipe; JSWorkerMessagePipe *ps = port->recv_pipe;
int ret; int ret;
@ -2403,13 +2542,85 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
return ret; return ret;
} }
#else #else
static int handle_posted_message(JSRuntime *rt, JSContext *ctx, static int handle_posted_message(JSContext *ctx, JSWorkerMessageHandler *port)
JSWorkerMessageHandler *port)
{ {
return 0; return 0;
} }
#endif /* !USE_WORKER */ #endif /* !USE_WORKER */
static void handle_socket_message(JSContext *ctx, JSOSSockHandler *sh)
{
#ifdef _WIN32
WSANETWORKEVENTS netEvents;
WSAEnumNetworkEvents(sh->sockfd, sh->event, &netEvents);
#endif
int err = 0;
struct sockaddr_storage sockaddr;
socklen_t addr_len = sizeof(sockaddr);
if (sh->magic == MAGIC_SOCKET_RECV) {
err = js_get_sockerrno(recv(sh->sockfd, (char*) sh->buffer, sh->length, 0));
} else if (sh->magic == MAGIC_SOCKET_SEND) {
err = js_get_sockerrno(send(sh->sockfd, (char*) sh->buffer, sh->length, 0));
} else if (sh->magic == MAGIC_SOCKET_RECVFROM) {
err = js_get_sockerrno(recvfrom(sh->sockfd, (char*) sh->buffer, sh->length, 0, (struct sockaddr *)&sockaddr, &addr_len));
} else if (sh->magic == MAGIC_SOCKET_SENDTO) {
err = js_get_sockerrno(sendto(sh->sockfd, (char*) sh->buffer, sh->length, 0, (const struct sockaddr *)&sh->sockaddr, addr_len));
} else if (sh->magic == MAGIC_SOCKET_CONNECT) {
#ifdef _WIN32
err = 0;
#else
socklen_t len = sizeof(err);
err = getsockopt(sh->sockfd, SOL_SOCKET, SO_ERROR, (char*) &err, &len) ? -errno : -err;
#endif
} else if (sh->magic == MAGIC_SOCKET_ACCEPT) {
err = js_get_sockerrno(accept(sh->sockfd, (struct sockaddr *)&sockaddr, &addr_len));
}
#ifdef _WIN32
if (err == -WSAEWOULDBLOCK)
return;
#else
if (err == -EAGAIN || err == -EWOULDBLOCK)
return;
#endif
JSValue promiseval = JS_UNDEFINED;
if (sh->magic == MAGIC_SOCKET_ACCEPT) {
promiseval = JS_NewArray(ctx);
if (JS_IsException(promiseval))
return;
JS_DefinePropertyValueUint32(ctx, promiseval, 0, JS_NewInt32(ctx, err), JS_PROP_C_W_E);
JS_DefinePropertyValueUint32(ctx, promiseval, 1, JS_ToSockaddrObj(ctx, &sockaddr), JS_PROP_C_W_E);
} else if (sh->magic == MAGIC_SOCKET_RECVFROM) {
JSValue addrObj = JS_ToSockaddrObj(ctx, &sockaddr);
if (JS_IsException(addrObj))
return;
promiseval = JS_NewArray(ctx);
if (JS_IsException(promiseval))
return;
JS_DefinePropertyValueUint32(ctx, promiseval, 0, JS_NewInt64(ctx, err), JS_PROP_C_W_E);
JS_DefinePropertyValueUint32(ctx, promiseval, 1, addrObj, JS_PROP_C_W_E);
} else {
promiseval = JS_NewInt32(ctx, err);
}
/* 'func' might be destroyed when calling itself (if it frees the
handler), so must take extra care */
JSValue func = JS_DupValue(ctx, err < 0 ? sh->reject : sh->resolve);
JSValue retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&promiseval);
JS_FreeValue(ctx, func);
if (JS_IsException(retval)) {
js_std_dump_error(ctx);
} else {
JS_FreeValue(ctx, retval);
}
JS_FreeValue(ctx, promiseval);
JS_FreeValue(ctx, sh->bufval);
free_sock_handler(JS_GetRuntime(ctx), sh);
}
#if defined(_WIN32) #if defined(_WIN32)
static int js_os_poll(JSContext *ctx) static int js_os_poll(JSContext *ctx)
@ -2424,8 +2635,10 @@ static int js_os_poll(JSContext *ctx)
/* XXX: handle signals if useful */ /* XXX: handle signals if useful */
if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && if (list_empty(&ts->os_rw_handlers) &&
list_empty(&ts->port_list)) { list_empty(&ts->os_timers) &&
list_empty(&ts->port_list) &&
list_empty(&ts->os_sock_handlers)) {
return -1; /* no more events */ return -1; /* no more events */
} }
@ -2455,13 +2668,21 @@ static int js_os_poll(JSContext *ctx)
count = 0; count = 0;
list_for_each(el, &ts->os_rw_handlers) { list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link); rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { if (rh->fd == 0 && !JS_IsNull(rh->r_func)) {
handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin
if (count == (int)countof(handles)) if (count == (int)countof(handles))
break; break;
} }
} }
list_for_each(el, &ts->os_sock_handlers) {
JSOSSockHandler *sh = list_entry(el, JSOSSockHandler, link);
//TODO: socket readability don't seems to be a winsock event => trigger manually
handles[count++] = sh->event;
if (count == (int)countof(handles))
break;
}
list_for_each(el, &ts->port_list) { list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (JS_IsNull(port->on_message_func)) if (JS_IsNull(port->on_message_func))
@ -2477,22 +2698,32 @@ static int js_os_poll(JSContext *ctx)
timeout = min_delay; timeout = min_delay;
ret = WaitForMultipleObjects(count, handles, FALSE, timeout); ret = WaitForMultipleObjects(count, handles, FALSE, timeout);
// why iterate on every list instead of just handles[ret] ?
if (ret < count) { if (ret < count) {
list_for_each(el, &ts->os_rw_handlers) { list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link); rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { if (rh->fd == 0 && !JS_IsNull(rh->r_func)) {
call_handler(ctx, rh->rw_func[0]); call_handler(ctx, rh->r_func);
/* must stop because the list may have been modified */ /* must stop because the list may have been modified */
goto done; goto done;
} }
} }
list_for_each(el, &ts->os_sock_handlers) {
JSOSSockHandler *sh = list_entry(el, JSOSSockHandler, link);
if (sh->event == handles[ret]) {
handle_socket_message(ctx, sh);
WSAResetEvent(sh->event); // WSACloseEvent(sh->event);
goto done;
}
}
list_for_each(el, &ts->port_list) { list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) { if (!JS_IsNull(port->on_message_func)) {
JSWorkerMessagePipe *ps = port->recv_pipe; JSWorkerMessagePipe *ps = port->recv_pipe;
if (ps->waker.handle == handles[ret]) { if (ps->waker.handle == handles[ret]) {
if (handle_posted_message(rt, ctx, port)) if (handle_posted_message(ctx, port))
goto done; goto done;
} }
} }
@ -2515,6 +2746,7 @@ static int js_os_poll(JSContext *ctx)
int64_t cur_time, delay; int64_t cur_time, delay;
fd_set rfds, wfds; fd_set rfds, wfds;
JSOSRWHandler *rh; JSOSRWHandler *rh;
JSOSSockHandler *sh;
struct list_head *el; struct list_head *el;
struct timeval tv, *tvp; struct timeval tv, *tvp;
@ -2535,7 +2767,9 @@ static int js_os_poll(JSContext *ctx)
} }
} }
if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && if (list_empty(&ts->os_rw_handlers) &&
list_empty(&ts->os_sock_handlers) &&
list_empty(&ts->os_timers) &&
list_empty(&ts->port_list)) list_empty(&ts->port_list))
return -1; /* no more events */ return -1; /* no more events */
@ -2571,12 +2805,27 @@ static int js_os_poll(JSContext *ctx)
list_for_each(el, &ts->os_rw_handlers) { list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link); rh = list_entry(el, JSOSRWHandler, link);
fd_max = max_int(fd_max, rh->fd); fd_max = max_int(fd_max, rh->fd);
if (!JS_IsNull(rh->rw_func[0])) if (!JS_IsNull(rh->r_func))
FD_SET(rh->fd, &rfds); FD_SET(rh->fd, &rfds);
if (!JS_IsNull(rh->rw_func[1])) if (!JS_IsNull(rh->w_func))
FD_SET(rh->fd, &wfds); FD_SET(rh->fd, &wfds);
} }
list_for_each(el, &ts->os_sock_handlers) {
sh = list_entry(el, JSOSSockHandler, link);
fd_max = max_int(fd_max, sh->sockfd);
if (sh->magic == MAGIC_SOCKET_ACCEPT ||
sh->magic == MAGIC_SOCKET_RECV ||
sh->magic == MAGIC_SOCKET_RECVFROM){
FD_SET(sh->sockfd, &rfds);
}
if (sh->magic == MAGIC_SOCKET_CONNECT ||
sh->magic == MAGIC_SOCKET_SEND ||
sh->magic == MAGIC_SOCKET_SENDTO){
FD_SET(sh->sockfd, &wfds);
}
}
list_for_each(el, &ts->port_list) { list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) { if (!JS_IsNull(port->on_message_func)) {
@ -2590,26 +2839,32 @@ static int js_os_poll(JSContext *ctx)
if (ret > 0) { if (ret > 0) {
list_for_each(el, &ts->os_rw_handlers) { list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link); rh = list_entry(el, JSOSRWHandler, link);
if (!JS_IsNull(rh->rw_func[0]) && if (!JS_IsNull(rh->r_func) && FD_ISSET(rh->fd, &rfds)) {
FD_ISSET(rh->fd, &rfds)) { call_handler(ctx, rh->r_func);
call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */ /* must stop because the list may have been modified */
goto done; goto done;
} }
if (!JS_IsNull(rh->rw_func[1]) && if (!JS_IsNull(rh->w_func) && FD_ISSET(rh->fd, &wfds)) {
FD_ISSET(rh->fd, &wfds)) { call_handler(ctx, rh->w_func);
call_handler(ctx, rh->rw_func[1]);
/* must stop because the list may have been modified */ /* must stop because the list may have been modified */
goto done; goto done;
} }
} }
list_for_each(el, &ts->os_sock_handlers) {
sh = list_entry(el, JSOSSockHandler, link);
if (FD_ISSET(sh->sockfd, &rfds) || FD_ISSET(sh->sockfd, &wfds)) {
handle_socket_message(ctx, sh);
goto done;
}
}
list_for_each(el, &ts->port_list) { list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) { if (!JS_IsNull(port->on_message_func)) {
JSWorkerMessagePipe *ps = port->recv_pipe; JSWorkerMessagePipe *ps = port->recv_pipe;
if (FD_ISSET(ps->waker.read_fd, &rfds)) { if (FD_ISSET(ps->waker.read_fd, &rfds)) {
if (handle_posted_message(rt, ctx, port)) if (handle_posted_message(ctx, port))
goto done; goto done;
} }
} }
@ -3421,6 +3676,265 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
#endif /* !_WIN32 */ #endif /* !_WIN32 */
static JSValue js_os_socket(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int domain, type, protocol = 0;
if (JS_ToInt32(ctx, &domain, argv[0]))
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &type, argv[1]))
return JS_EXCEPTION;
if (argc >= 3 && JS_ToInt32(ctx, &protocol, argv[2]))
return JS_EXCEPTION;
int socketfd = socket(domain, type, protocol);
int ret = js_get_sockerrno(socketfd);
if (ret < 0)
return JS_NewInt32(ctx, ret);
#if defined(_WIN32)
u_long mode = 1;
ret = ioctlsocket(ret, FIONBIO, &mode);
#else
ret = fcntl(ret, F_SETFL, fcntl(ret, F_GETFL, 0) | O_NONBLOCK);
#endif
if (ret < 0)
return JS_NewInt32(ctx, ret);
return JS_NewInt32(ctx, socketfd);
}
static JSValue js_os_get_setsockopt(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
int sock, optname, ret;
uint8_t *optval;
socklen_t optlen;
size_t buflen;
if (JS_ToInt32(ctx, &sock, argv[0]))
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &optname, argv[1]))
return JS_EXCEPTION;
optval = JS_GetArrayBuffer(ctx, &buflen, argv[2]);
if (!optval)
return JS_EXCEPTION;
optlen = buflen;
if (magic == 0)
ret = js_get_sockerrno(getsockopt(sock, SOL_SOCKET, optname, (char*)optval, &optlen));
else
ret = js_get_sockerrno(setsockopt(sock, SOL_SOCKET, optname, (char*)optval, optlen));
return JS_NewInt32(ctx, ret);
}
static JSValue js_os_getaddrinfo(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int ret = -1;
if (!JS_IsString(argv[0]))
return JS_EXCEPTION;
const char* node = JS_ToCString(ctx, argv[0]);
const char* service = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
if (!JS_IsNull(argv[1]) && JS_IsObject(argv[1])) {
JSValue prop_service = JS_GetPropertyStr(ctx, argv[1], "service");
JSValue prop_family = JS_GetPropertyStr(ctx, argv[1], "family");
JSValue prop_socktype = JS_GetPropertyStr(ctx, argv[1], "socktype");
if (JS_IsException(prop_service) ||
JS_IsException(prop_family) ||
JS_IsException(prop_socktype))
goto fail;
if (!JS_IsUndefined(prop_service))
service = JS_ToCString(ctx, prop_service);
if (!JS_IsUndefined(prop_family))
JS_ToInt32(ctx, &hints.ai_family, prop_family);
if (!JS_IsUndefined(prop_socktype))
JS_ToInt32(ctx, &hints.ai_socktype, prop_socktype);
}
struct addrinfo *ai;
ret = js_get_sockerrno(getaddrinfo(node, service, &hints, &ai));
if (ret)
goto fail;
struct addrinfo *it;
socklen_t objLen;
JSValue obj = JS_NewArray(ctx);
for (objLen = 0, it = ai; it; it = it->ai_next, objLen++) {
JSValue addrObj = JS_ToSockaddrObj(ctx,(struct sockaddr_storage *)it->ai_addr);
JSValue prop_socktype = JS_NewUint32(ctx, it->ai_socktype);
JS_DefinePropertyValueStr(ctx, addrObj, "socktype", prop_socktype, JS_PROP_C_W_E);
JS_SetPropertyUint32(ctx,obj,objLen,addrObj);
}
freeaddrinfo(ai);
JS_FreeCString(ctx, service);
JS_FreeCString(ctx, node);
return obj;
fail:
JS_FreeValue(ctx, obj);
JS_FreeCString(ctx, service);
JS_FreeCString(ctx, node);
return JS_NewInt32(ctx, ret);
}
static JSValue js_os_bind(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int sockfd, ret = 0;
struct sockaddr_storage sockaddr;
if (JS_ToInt32(ctx, &sockfd, argv[0]))
return JS_EXCEPTION;
if (JS_ToSockaddrStruct(ctx, argv[1], &sockaddr, sockfd))
return JS_EXCEPTION;
socklen_t addr_len = sockaddr.ss_family == AF_INET ? sizeof(struct sockaddr_in) :
sockaddr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0;
ret = js_get_sockerrno(bind(sockfd, (struct sockaddr *)&sockaddr, addr_len));
return JS_NewInt32(ctx, ret);
}
static JSValue js_os_listen(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int sockfd, backlog = SOMAXCONN, ret;
if (JS_ToInt32(ctx, &sockfd, argv[0]))
return JS_EXCEPTION;
if (argc >= 2 && JS_ToInt32(ctx, &backlog, argv[1]))
return JS_EXCEPTION;
ret = js_get_sockerrno(listen(sockfd, backlog));
return JS_NewInt32(ctx, ret);
}
static JSValue js_os_connect_accept(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
int sockfd, sockret = 0;
if (JS_ToInt32(ctx, &sockfd, argv[0]))
return JS_EXCEPTION;
struct sockaddr_storage sockaddr;
if (magic == MAGIC_SOCKET_CONNECT) {
if (JS_ToSockaddrStruct(ctx, argv[1], &sockaddr, sockfd) < 0){
JS_ThrowTypeError(ctx, "invalid sockaddr");
return JS_EXCEPTION;
}
socklen_t addr_len = sockaddr.ss_family == AF_INET ? sizeof(struct sockaddr_in) :
sockaddr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0;
sockret = js_get_sockerrno(connect(sockfd, (struct sockaddr *)&sockaddr, addr_len));
#if defined(_WIN32)
if (sockret != -WSAEWOULDBLOCK)
#else
if (sockret != -EINPROGRESS)
#endif
{
JS_ThrowTypeError(ctx, "connect failed (%i)", sockret);
return JS_EXCEPTION;
}
}
/* accept() pooling done in handle_socket_message() to avoid code duplicate */
JSValue resolving_funcs[2];
JSValue promise = JS_NewPromiseCapability(ctx, resolving_funcs);
if (JS_IsException(promise))
return JS_EXCEPTION;
JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
JSOSSockHandler *sh = js_mallocz(ctx, sizeof(*sh));
sh->sockfd = sockfd;
sh->magic = magic;
sh->resolve = JS_DupValue(ctx, resolving_funcs[0]);
sh->reject = JS_DupValue(ctx, resolving_funcs[1]);
#if defined(_WIN32)
sh->event = WSACreateEvent();
WSAEventSelect(sh->sockfd, sh->event, magic == MAGIC_SOCKET_CONNECT ? FD_CONNECT : FD_ACCEPT);
#endif
list_add_tail(&sh->link, &ts->os_sock_handlers);
JS_FreeValue(ctx, resolving_funcs[0]);
JS_FreeValue(ctx, resolving_funcs[1]);
return promise;
}
static JSValue js_os_recv_send(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
JSOSSockHandler *sh = js_mallocz(ctx, sizeof(*sh));
if (!sh)
return JS_EXCEPTION;
JSValue resolving_funcs[2];
JSValue promise = JS_NewPromiseCapability(ctx, resolving_funcs);
if (JS_IsException(promise))
goto fail;
// store all our info for async processing
sh->magic = magic;
if (JS_ToInt32(ctx, &sh->sockfd, argv[0]))
goto fail;
unsigned bufArgvIdx = 1; // buffer is argv[1], unless sendto()
if (magic == MAGIC_SOCKET_SENDTO) {
bufArgvIdx = 2;
if (JS_ToSockaddrStruct(ctx, argv[1], &sh->sockaddr, sh->sockfd) < 0) {
JS_ThrowTypeError(ctx, "invalid sockaddr");
goto fail;
}
}
sh->buffer = JS_GetArrayBuffer(ctx, &sh->length, argv[bufArgvIdx]);
if (!sh->buffer)
goto fail;
if (argc > bufArgvIdx + 1) {
size_t length;
if (JS_ToIndex(ctx, &length, argv[bufArgvIdx + 1])) {
JS_ThrowRangeError(ctx, "Invalid length given");
goto fail;
}
if (length > sh->length) {
JS_ThrowRangeError(ctx, "recv/send array buffer overflow");
goto fail;
}
sh->length = length;
}
#if defined(_WIN32)
sh->event = WSACreateEvent();
int flags = (magic == MAGIC_SOCKET_SENDTO || magic == MAGIC_SOCKET_SEND) ? FD_WRITE : FD_READ;
WSAEventSelect(sh->sockfd, sh->event, flags);
#endif
sh->bufval = JS_DupValue(ctx, argv[bufArgvIdx]);
sh->resolve = JS_DupValue(ctx, resolving_funcs[0]);
sh->reject = JS_DupValue(ctx, resolving_funcs[1]);
list_add_tail(&sh->link, &ts->os_sock_handlers);
JS_FreeValue(ctx, resolving_funcs[0]);
JS_FreeValue(ctx, resolving_funcs[1]);
return promise;
fail:
JS_FreeValue(ctx, promise);
JS_FreeValue(ctx, resolving_funcs[0]);
JS_FreeValue(ctx, resolving_funcs[1]);
js_free(ctx, sh);
return JS_EXCEPTION;
}
static JSValue js_os_shutdown(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int sockfd, how, ret;
if (JS_ToInt32(ctx, &sockfd, argv[0]))
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &how, argv[1]))
return JS_EXCEPTION;
ret = js_get_sockerrno(shutdown(sockfd, how));
return JS_NewInt32(ctx, ret);
}
#ifdef USE_WORKER #ifdef USE_WORKER
/* Worker */ /* Worker */
@ -3895,8 +4409,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ),
JS_CFUNC_DEF("remove", 1, js_os_remove ), JS_CFUNC_DEF("remove", 1, js_os_remove ),
JS_CFUNC_DEF("rename", 2, js_os_rename ), JS_CFUNC_DEF("rename", 2, js_os_rename ),
JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadWriteHandler, 0 ),
JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadWriteHandler, 1 ),
JS_CFUNC_DEF("signal", 2, js_os_signal ), JS_CFUNC_DEF("signal", 2, js_os_signal ),
OS_FLAG(SIGINT), OS_FLAG(SIGINT),
OS_FLAG(SIGABRT), OS_FLAG(SIGABRT),
@ -3955,6 +4469,37 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("kill", 2, js_os_kill ), JS_CFUNC_DEF("kill", 2, js_os_kill ),
JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup", 1, js_os_dup ),
JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),
#endif
/* SOCKET I/O */
JS_CFUNC_DEF("socket", 2, js_os_socket ),
JS_CFUNC_MAGIC_DEF("getsockopt", 3, js_os_get_setsockopt, 0 ),
JS_CFUNC_MAGIC_DEF("setsockopt", 3, js_os_get_setsockopt, 1 ),
JS_CFUNC_DEF("getaddrinfo", 2, js_os_getaddrinfo ),
JS_CFUNC_DEF("bind", 2, js_os_bind ),
JS_CFUNC_DEF("listen", 1, js_os_listen ),
JS_CFUNC_MAGIC_DEF("accept", 1, js_os_connect_accept, MAGIC_SOCKET_ACCEPT ),
JS_CFUNC_MAGIC_DEF("connect", 2, js_os_connect_accept, MAGIC_SOCKET_CONNECT ),
JS_CFUNC_MAGIC_DEF("recv", 3, js_os_recv_send, MAGIC_SOCKET_RECV ),
JS_CFUNC_MAGIC_DEF("send", 3, js_os_recv_send, MAGIC_SOCKET_SEND ),
JS_CFUNC_MAGIC_DEF("recvfrom", 4, js_os_recv_send, MAGIC_SOCKET_RECVFROM ),
JS_CFUNC_MAGIC_DEF("sendto", 3, js_os_recv_send, MAGIC_SOCKET_SENDTO ),
JS_CFUNC_DEF("shutdown", 2, js_os_shutdown ),
OS_FLAG(AF_INET),
OS_FLAG(AF_INET6),
OS_FLAG(SOCK_STREAM),
OS_FLAG(SOCK_DGRAM),
OS_FLAG(SOCK_RAW),
OS_FLAG(SO_REUSEADDR),
OS_FLAG(SO_RCVBUF),
OS_FLAG(SO_ERROR),
#if defined(_WIN32)
JS_PROP_INT32_DEF("SHUT_RD", SD_RECEIVE, JS_PROP_CONFIGURABLE),
JS_PROP_INT32_DEF("SHUT_WR", SD_SEND, JS_PROP_CONFIGURABLE),
JS_PROP_INT32_DEF("SHUT_RDWR", SD_BOTH, JS_PROP_CONFIGURABLE),
#else
OS_FLAG(SHUT_RD),
OS_FLAG(SHUT_WR),
OS_FLAG(SHUT_RDWR),
#endif #endif
}; };
@ -4090,6 +4635,7 @@ void js_std_init_handlers(JSRuntime *rt)
} }
memset(ts, 0, sizeof(*ts)); memset(ts, 0, sizeof(*ts));
init_list_head(&ts->os_rw_handlers); init_list_head(&ts->os_rw_handlers);
init_list_head(&ts->os_sock_handlers);
init_list_head(&ts->os_signal_handlers); init_list_head(&ts->os_signal_handlers);
init_list_head(&ts->os_timers); init_list_head(&ts->os_timers);
init_list_head(&ts->port_list); init_list_head(&ts->port_list);
@ -4121,6 +4667,11 @@ void js_std_free_handlers(JSRuntime *rt)
free_rw_handler(rt, rh); free_rw_handler(rt, rh);
} }
list_for_each_safe(el, el1, &ts->os_sock_handlers) {
JSOSSockHandler *sh = list_entry(el, JSOSSockHandler, link);
free_sock_handler(rt, sh);
}
list_for_each_safe(el, el1, &ts->os_signal_handlers) { list_for_each_safe(el, el1, &ts->os_signal_handlers) {
JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link);
free_sh(rt, sh); free_sh(rt, sh);

1558
quickjs.c

File diff suppressed because it is too large Load Diff

View File

@ -174,7 +174,7 @@ cp Makefile VERSION TODO Changelog readme.txt LICENSE \
cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests
cp examples/*.js examples/*.c $outdir/examples cp examples/*.js examples/*.c examples/*.json $outdir/examples
cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \ cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \
$outdir/doc $outdir/doc

View File

@ -140,7 +140,7 @@ Intl.NumberFormat-v3=skip
Intl.RelativeTimeFormat=skip Intl.RelativeTimeFormat=skip
Intl.Segmenter=skip Intl.Segmenter=skip
IsHTMLDDA IsHTMLDDA
iterator-helpers=skip iterator-helpers
iterator-sequencing=skip iterator-sequencing=skip
json-modules json-modules
json-parse-with-source=skip json-parse-with-source=skip
@ -183,7 +183,7 @@ RegExp.escape
resizable-arraybuffer=skip resizable-arraybuffer=skip
rest-parameters rest-parameters
Set Set
set-methods=skip set-methods
ShadowRealm=skip ShadowRealm=skip
SharedArrayBuffer SharedArrayBuffer
source-phase-imports-module-source=skip source-phase-imports-module-source=skip
@ -304,14 +304,10 @@ test262/test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js
# spec updates it in this case) # spec updates it in this case)
test262/test/staging/sm/Array/frozen-dense-array.js test262/test/staging/sm/Array/frozen-dense-array.js
# does not match spec
test262/test/staging/sm/Iterator/from/wrap-next-not-object-throws.js
# not supported # not supported
test262/test/staging/sm/Set/difference.js
test262/test/staging/sm/Set/intersection.js
test262/test/staging/sm/Set/is-disjoint-from.js
test262/test/staging/sm/Set/is-subset-of.js
test262/test/staging/sm/Set/is-superset-of.js
test262/test/staging/sm/Set/symmetric-difference.js
test262/test/staging/sm/Set/union.js
test262/test/staging/sm/extensions/censor-strict-caller.js test262/test/staging/sm/extensions/censor-strict-caller.js
test262/test/staging/sm/JSON/parse-with-source.js test262/test/staging/sm/JSON/parse-with-source.js