@bytecodealliance/preview2-shim
Advanced tools
Comparing version 0.0.7 to 0.0.8
@@ -0,3 +1,17 @@ | ||
let _env; | ||
export function _setEnv (envObj) { | ||
_env = Object.entries(envObj); | ||
} | ||
export function getEnvironment () { | ||
if (!_env) _env = []; | ||
return _env; | ||
} | ||
export function preopens () { | ||
return []; | ||
} | ||
export function getArguments () { | ||
return []; | ||
} |
@@ -1,2 +0,2 @@ | ||
import * as logging from "./logging.js"; | ||
import * as console from "./console.js"; | ||
import * as defaultOutgoingHttp from "./default-outgoing-HTTP.js"; | ||
@@ -6,4 +6,6 @@ import * as environment from "./environment.js"; | ||
import * as filesystem from "./filesystem.js"; | ||
import * as http from "./http.js"; | ||
import * as instanceNetwork from "./instance-network.js"; | ||
import * as ipNameLookup from "./ip-name-lookup.js"; | ||
import * as monotonicClock from "./monotonic-clock.js"; | ||
import * as network from "./network.js"; | ||
import * as poll from "./poll.js"; | ||
@@ -13,17 +15,31 @@ import * as preopens from "./preopens.js"; | ||
import * as streams from "./streams.js"; | ||
import * as tcpCreateSocket from "./tcp-create-socket.js"; | ||
import * as tcp from "./tcp.js"; | ||
import * as timezone from "./timezone.js"; | ||
import * as types from "./types.js"; | ||
import * as udpCreateSocket from "./udp-create-socket.js"; | ||
import * as udp from "./udp.js"; | ||
import * as wallClock from "./wall-clock.js"; | ||
export const importObject = { | ||
"default-outgoing-HTTP": defaultOutgoingHttp, | ||
"environment": environment, | ||
"exit": exit, | ||
"filesystem": filesystem, | ||
"http": http, | ||
"logging": logging, | ||
"monotonic-clock": monotonicClock, | ||
"poll": poll, | ||
"preopens": preopens, | ||
"random": random, | ||
"streams": streams, | ||
"wall-clock": wallClock, | ||
'console': console, | ||
'default-outgoing-HTTP': defaultOutgoingHttp, | ||
'environment': environment, | ||
'exit': exit, | ||
'filesystem': filesystem, | ||
'instance-network': instanceNetwork, | ||
'ip-name-lookup': ipNameLookup, | ||
'monotonic-clock': monotonicClock, | ||
'network': network, | ||
'poll': poll, | ||
'preopens': preopens, | ||
'random': random, | ||
'streams': streams, | ||
'tcp-create-socket': tcpCreateSocket, | ||
'tcp': tcp, | ||
'timezone': timezone, | ||
'types': types, | ||
'udp-create-socket': udpCreateSocket, | ||
'udp': udp, | ||
'wall-clock': wallClock | ||
}; | ||
@@ -30,0 +46,0 @@ |
@@ -6,31 +6,10 @@ | ||
let hrStart = hrtimeBigint(); | ||
export function now(clock) { | ||
if (clock === 0) { | ||
return hrtimeBigint() - hrStart; | ||
} | ||
console.log(`[monotonic clock] Unknown clock ${clock}`); | ||
function _hrtimeBigint () { | ||
return BigInt(Math.floor(performance.now() * 1e9)); | ||
} | ||
function hrtime(previousTimestamp) { | ||
const baseNow = Math.floor((Date.now() - performance.now()) * 1e-3); | ||
const clocktime = performance.now() * 1e-3; | ||
let seconds = Math.floor(clocktime) + baseNow; | ||
let nanoseconds = Math.floor((clocktime % 1) * 1e9); | ||
let _hrStart = _hrtimeBigint(); | ||
if (previousTimestamp) { | ||
seconds = seconds - previousTimestamp[0]; | ||
nanoseconds = nanoseconds - previousTimestamp[1]; | ||
if (nanoseconds < 0) { | ||
seconds--; | ||
nanoseconds += 1e9; | ||
} | ||
} | ||
return [seconds, nanoseconds]; | ||
export function now () { | ||
return _hrtimeBigint() - _hrStart; | ||
} | ||
function hrtimeBigint(time) { | ||
const diff = hrtime(time); | ||
return BigInt(diff[0] * 1e9 + diff[1]); | ||
} |
@@ -1,12 +0,9 @@ | ||
export function now(clock) { | ||
if (clock === 1) { | ||
const seconds = BigInt(Math.floor(Date.now() / 1000)); | ||
const nanoseconds = (Date.now() % 1000) * 1000 * 1000; | ||
return { seconds, nanoseconds }; | ||
} | ||
console.log(`[wall-clock] Unknown clock ${clock}`); | ||
export function now() { | ||
const seconds = BigInt(Math.floor(Date.now() / 1e3)); | ||
const nanoseconds = (Date.now() % 1e3) * 1e6; | ||
return { seconds, nanoseconds }; | ||
} | ||
export function resolution(clock) { | ||
console.log(`[wall-clock] Wall clock resolution ${clock}`); | ||
export function resolution() { | ||
console.log(`[wall-clock] Wall clock resolution`); | ||
} |
@@ -0,3 +1,11 @@ | ||
import process from 'node:process'; | ||
let _env; | ||
export function _setEnv (envObj) { | ||
_env = Object.entries(envObj); | ||
} | ||
export function getEnvironment () { | ||
return []; | ||
if (!_env) _setEnv(process.env); | ||
return _env; | ||
} | ||
@@ -7,2 +15,6 @@ | ||
return []; | ||
} | ||
export function getArguments () { | ||
return []; | ||
} |
export function exit(status) { | ||
console.log(`[exit] Exit: ${JSON.stringify(status)}`); | ||
process.exit(status.tag === 'err' ? 1 : 0); | ||
} |
@@ -1,14 +0,7 @@ | ||
// export interface DescriptorStat { | ||
// dev: Device, | ||
// ino: Inode, | ||
// type: DescriptorType, | ||
// nlink: Linkcount, | ||
// size: Filesize, | ||
// atim: Timestamp, | ||
// mtim: Timestamp, | ||
// ctim: Timestamp, | ||
// } | ||
import { openSync, constants, statSync, lstatSync, fstatSync, closeSync, readdirSync } from 'node:fs'; | ||
import { _descriptors, _addOpenedDescriptor, _removeOpenedDescriptor, _getDescriptorType, _setSubdescriptorType, _setDescriptorType, _getFullPath } from './preopens.js'; | ||
import { _createFileStream } from './streams.js'; | ||
export function readViaStream(fd, offset) { | ||
console.log(`[filesystem] READ STREAM ${fd} ${offset}`); | ||
return _createFileStream(fd, offset); | ||
} | ||
@@ -37,3 +30,8 @@ | ||
export function getType(fd) { | ||
console.log(`[filesystem] GET TYPE ${fd}`); | ||
let type = _getDescriptorType(fd); | ||
if (type === null) { | ||
stat(fd); | ||
type = _getDescriptorType(fd); | ||
} | ||
return type; | ||
} | ||
@@ -61,4 +59,14 @@ | ||
let _dirStreams = []; | ||
export function readDirectory(fd) { | ||
console.log(`[filesystem] READ DIR`, fd); | ||
const fullPath = _getFullPath(fd); | ||
let dirs; | ||
try { | ||
dirs = readdirSync(fullPath, { withFileTypes: true }); | ||
} | ||
catch (e) { | ||
_convertFsError(e); | ||
} | ||
_dirStreams.push({ fd, dirs, cursor: 0 }); | ||
return _dirStreams.length - 1; | ||
} | ||
@@ -74,8 +82,114 @@ | ||
const nsMagnitude = 1_000_000_000_000n; | ||
function nsToDateTime (ns) { | ||
const seconds = ns / nsMagnitude; | ||
const nanoseconds = Number(ns % seconds); | ||
return { seconds, nanoseconds }; | ||
} | ||
export function _convertFsError (e) { | ||
switch (e.code) { | ||
case 'EACCES': throw 'access'; | ||
case 'EAGAIN': | ||
case 'EWOULDBLOCK': throw 'would-block'; | ||
case 'EALREADY': throw 'already'; | ||
case 'EBADF': throw 'bad-descriptor'; | ||
case 'EBUSY': throw 'busy'; | ||
case 'EDEADLK': throw 'deadlock'; | ||
case 'EDQUOT': throw 'quota'; | ||
case 'EEXIST': throw 'exist'; | ||
case 'EFBIG': throw 'file-too-large'; | ||
case 'EILSEQ': throw 'illegal-byte-sequence'; | ||
case 'EINPROGRESS': throw 'in-progress'; | ||
case 'EINTR': throw 'interrupted'; | ||
case 'EINVAL': throw 'invalid'; | ||
case 'EIO': throw 'io'; | ||
case 'EISDIR': throw 'is-directory'; | ||
case 'ELOOP': throw 'loop'; | ||
case 'EMLINK': throw 'too-many-links'; | ||
case 'EMSGSIZE': throw 'message-size'; | ||
case 'ENAMETOOLONG': throw 'name-too-long' | ||
case 'ENODEV': throw 'no-device'; | ||
case 'ENOENT': throw 'no-entry'; | ||
case 'ENOLCK': throw 'no-lock'; | ||
case 'ENOMEM': throw 'insufficient-memory'; | ||
case 'ENOSPC': throw 'insufficient-space'; | ||
case 'ENOTDIR': throw 'not-directory'; | ||
case 'ENOTEMPTY': throw 'not-empty'; | ||
case 'ENOTRECOVERABLE': throw 'not-recoverable'; | ||
case 'ENOTSUP': throw 'unsupported'; | ||
case 'ENOTTY': throw 'no-tty'; | ||
case 'ENXIO': throw 'no-such-device'; | ||
case 'EOVERFLOW': throw 'overflow'; | ||
case 'EPERM': throw 'not-permitted'; | ||
case 'EPIPE': throw 'pipe'; | ||
case 'EROFS': throw 'read-only'; | ||
case 'ESPIPE': throw 'invalid-seek'; | ||
case 'ETXTBSY': throw 'text-file-busy'; | ||
case 'EXDEV': throw 'cross-device'; | ||
default: throw e; | ||
} | ||
} | ||
function _lookupType (obj) { | ||
if (obj.isFile()) | ||
return 'regular-file'; | ||
else if (obj.isSocket()) | ||
return 'socket'; | ||
else if (obj.isSymbolicLink()) | ||
return 'symbolic-link'; | ||
else if (obj.isFIFO()) | ||
return 'fifo'; | ||
else if (obj.isDirectory()) | ||
return 'directory'; | ||
else if (obj.isCharacterDevice()) | ||
return 'character-device'; | ||
else if (obj.isBlockDevice()) | ||
return 'block-device'; | ||
return 'unknown'; | ||
} | ||
export function stat(fd) { | ||
console.log(`[filesystem] STAT`, fd); | ||
let stats; | ||
try { | ||
stats = fstatSync(fd, { bigint: true }); | ||
} | ||
catch (e) { | ||
_convertFsError(e); | ||
} | ||
const type = _lookupType(stats); | ||
_setDescriptorType(fd, type); | ||
return { | ||
device: stats.dev, | ||
inode: stats.ino, | ||
type, | ||
linkCount: stats.nlink, | ||
size: stats.size, | ||
dataAccessTimestamp: nsToDateTime(stats.atimeNs), | ||
dataModificationTimestamp: nsToDateTime(stats.mtimeNs), | ||
statusChangeTimestamp: nsToDateTime(stats.ctimeNs), | ||
}; | ||
} | ||
export function statAt(fd, pathFlags, path) { | ||
console.log(`[filesystem] STAT`, fd, pathFlags, path); | ||
export function statAt(fd, { symlinkFollow }, path) { | ||
const fullPath = _descriptors[fd].path + path; | ||
let stats; | ||
try { | ||
stats = (symlinkFollow ? statSync : lstatSync)(fullPath, { bigint: true }); | ||
} | ||
catch (e) { | ||
_convertFsError(e); | ||
} | ||
const type = _lookupType(stats); | ||
_setSubdescriptorType(fd, path, type); | ||
return { | ||
device: stats.dev, | ||
inode: stats.ino, | ||
type, | ||
linkCount: stats.nlink, | ||
size: stats.size, | ||
dataAccessTimestamp: nsToDateTime(stats.atimeNs), | ||
dataModificationTimestamp: nsToDateTime(stats.mtimeNs), | ||
statusChangeTimestamp: nsToDateTime(stats.ctimeNs), | ||
}; | ||
} | ||
@@ -91,4 +205,41 @@ | ||
export function openAt(fd) { | ||
console.log(`[filesystem] OPEN AT`, fd); | ||
export function openAt(fd, pathFlags, path, openFlags, descriptorFlags, modes) { | ||
// TODO | ||
// if (pathFlags.symlinkFollow) { | ||
// // resolve symlink | ||
// } | ||
const fullPath = _descriptors[fd].path + path; | ||
let fsOpenFlags = 0x0; | ||
if (openFlags.create) | ||
fsOpenFlags |= constants.O_CREAT; | ||
if (openFlags.directory) | ||
fsOpenFlags |= constants.O_DIRECTORY; | ||
if (openFlags.exclusive) | ||
fsOpenFlags |= constants.O_EXCL; | ||
if (openFlags.truncate) | ||
fsOpenFlags |= constants.O_TRUNC; | ||
if (descriptorFlags.read && descriptorFlags.write) | ||
fsOpenFlags |= constants.O_RDWR; | ||
else if (descriptorFlags.write) | ||
fsOpenFlags |= constants.O_WRONLY; | ||
// if (descriptorFlags.fileIntegritySync) | ||
// if (descriptorFlags.dataIntegritySync) | ||
// if (descriptorFlags.requestedWriteSync) | ||
// if (descriptorFlags.mutateDirectory) | ||
let fsMode = 0x0; | ||
if (modes.readable) | ||
fsMode |= 0o444; | ||
if (modes.writeable) | ||
fsMode |= 0o222; | ||
if (modes.executable) | ||
fsMode |= 0o111; | ||
let localFd; | ||
try { | ||
localFd = openSync(fullPath, fsOpenFlags, fsMode); | ||
} | ||
catch (e) { | ||
_convertFsError(e); | ||
} | ||
_addOpenedDescriptor(localFd, path, fd); | ||
return localFd; | ||
} | ||
@@ -145,11 +296,18 @@ | ||
export function dropDescriptor(fd) { | ||
console.log(`[filesystem] DROP DESCRIPTOR`, fd); | ||
_removeOpenedDescriptor(fd); | ||
closeSync(fd); | ||
} | ||
export function readDirectoryEntry(stream) { | ||
console.log(`[filesystem] READ DIRECTRY ENTRY`, stream); | ||
const streamValue = _dirStreams[stream]; | ||
if (streamValue.cursor === streamValue.dirs.length) | ||
return null; | ||
const dir = streamValue.dirs[streamValue.cursor++]; | ||
const type = _lookupType(dir); | ||
_setSubdescriptorType(streamValue.fd, '/' + dir.name, type); | ||
return { inode: null, type, name: dir.name }; | ||
} | ||
export function dropDirectoryEntryStream(stream) { | ||
console.log(`[filesystem] DROP DIRECTORY ENTRY`, stream); | ||
_dirStreams.splice(stream, 1); | ||
} |
@@ -1,2 +0,2 @@ | ||
import * as logging from "./logging.js"; | ||
import * as console from "./console.js"; | ||
import * as defaultOutgoingHttp from "./default-outgoing-HTTP.js"; | ||
@@ -6,4 +6,6 @@ import * as environment from "./environment.js"; | ||
import * as filesystem from "./filesystem.js"; | ||
import * as http from "./http.js"; | ||
import * as instanceNetwork from "./instance-network.js"; | ||
import * as ipNameLookup from "./ip-name-lookup.js"; | ||
import * as monotonicClock from "./monotonic-clock.js"; | ||
import * as network from "./network.js"; | ||
import * as poll from "./poll.js"; | ||
@@ -13,17 +15,31 @@ import * as preopens from "./preopens.js"; | ||
import * as streams from "./streams.js"; | ||
import * as tcpCreateSocket from "./tcp-create-socket.js"; | ||
import * as tcp from "./tcp.js"; | ||
import * as timezone from "./timezone.js"; | ||
import * as types from "./types.js"; | ||
import * as udpCreateSocket from "./udp-create-socket.js"; | ||
import * as udp from "./udp.js"; | ||
import * as wallClock from "./wall-clock.js"; | ||
export const importObject = { | ||
"default-outgoing-HTTP": defaultOutgoingHttp, | ||
"environment": environment, | ||
"exit": exit, | ||
"filesystem": filesystem, | ||
"http": http, | ||
"logging": logging, | ||
"monotonic-clock": monotonicClock, | ||
"poll": poll, | ||
"preopens": preopens, | ||
"random": random, | ||
"streams": streams, | ||
"wall-clock": wallClock, | ||
'console': console, | ||
'default-outgoing-HTTP': defaultOutgoingHttp, | ||
'environment': environment, | ||
'exit': exit, | ||
'filesystem': filesystem, | ||
'instance-network': instanceNetwork, | ||
'ip-name-lookup': ipNameLookup, | ||
'monotonic-clock': monotonicClock, | ||
'network': network, | ||
'poll': poll, | ||
'preopens': preopens, | ||
'random': random, | ||
'streams': streams, | ||
'tcp-create-socket': tcpCreateSocket, | ||
'tcp': tcp, | ||
'timezone': timezone, | ||
'types': types, | ||
'udp-create-socket': udpCreateSocket, | ||
'udp': udp, | ||
'wall-clock': wallClock | ||
}; | ||
@@ -30,0 +46,0 @@ |
import { hrtime } from "node:process"; | ||
export function resolution() { | ||
console.log(`[monotonic-clock] Monotonic clock resolution`); | ||
export function resolution () { | ||
return 1n; | ||
} | ||
let hrStart = hrtime.bigint(); | ||
export function now() { | ||
return hrtime.bigint() - hrStart; | ||
let _hrStart = hrtime.bigint(); | ||
export function now () { | ||
return hrtime.bigint() - _hrStart; | ||
} | ||
@@ -11,0 +11,0 @@ |
@@ -0,1 +1,59 @@ | ||
// default is full permissions | ||
let preopenCnt = 4; | ||
export let _descriptors = { | ||
3: { type: 'directory', path: '/', parent: null, subpathTypes: {} } | ||
}; | ||
let directories = [[3, '/']]; | ||
export function _getFullPath (fd) { | ||
let path = ''; | ||
while (fd) { | ||
path = _descriptors[fd].path + path; | ||
fd = _descriptors[fd].parent; | ||
} | ||
return path; | ||
} | ||
export function _getDescriptorType (fd) { | ||
return _descriptors[fd].type; | ||
} | ||
export function _setDescriptorType (fd, type) { | ||
_descriptors[fd].type = type; | ||
} | ||
export function _setSubdescriptorType (fd, path, type) { | ||
while (_descriptors[fd].parent) { | ||
path = _descriptors[fd].path + path; | ||
fd = _descriptors[fd].parent; | ||
} | ||
_descriptors[fd].subpathTypes[path] = type; | ||
} | ||
export function _addOpenedDescriptor (fd, path, parentFd) { | ||
if (fd < preopenCnt || _descriptors[fd]) | ||
throw 'bad-descriptor'; | ||
let type = null; | ||
for (const [_path, _type] of Object.entries(_descriptors[parentFd].subpathTypes)) { | ||
if (_path === path) | ||
type = _type; | ||
} | ||
_descriptors[fd] = { path, type, parent: parentFd, subpathTypes: {} }; | ||
} | ||
export function _removeOpenedDescriptor (fd) { | ||
if (fd < preopenCnt) | ||
throw 'eperm'; | ||
delete _descriptors[fd]; | ||
} | ||
export function _setPreopens (preopens) { | ||
_descriptors = {}; | ||
directories = [,,]; | ||
for (const [virtualPath, path] of Object.entries(preopens)) { | ||
_descriptors[preopenCnt] = { type: 'directory', path, parent: null, subpathTypes: {} }; | ||
directories.push([preopenCnt++, virtualPath]); | ||
} | ||
} | ||
export function getStdio () { | ||
@@ -10,3 +68,3 @@ return { | ||
export function getDirectories () { | ||
return []; | ||
return directories; | ||
} |
@@ -0,1 +1,16 @@ | ||
import { readSync as fsReadSync } from 'node:fs'; | ||
import { _convertFsError } from './filesystem.js'; | ||
export let _streams = {}; | ||
let streamCnt = 0; | ||
export function _createFileStream(fd, offset) { | ||
// note we only support offset 0 | ||
if (Number(offset) === 0) | ||
_streams[streamCnt] = { | ||
type: 'file', | ||
fd: fd | ||
}; | ||
return streamCnt++; | ||
} | ||
export function read(s, len) { | ||
@@ -9,4 +24,22 @@ switch (s) { | ||
} | ||
export function blockingRead(s, _len) { | ||
console.log(`[streams] Blocking read ${s}`); | ||
export function blockingRead(s, len) { | ||
len = Number(len); | ||
const stream = _streams[s]; | ||
if (!stream) throw null; | ||
switch (stream.type) { | ||
case 'file': { | ||
const buf = Buffer.alloc(Number(len)); | ||
try { | ||
const readBytes = fsReadSync(stream.fd, buf, 0, Number(len)); | ||
if (readBytes < Number(len)) | ||
return [new Uint8Array(), true]; | ||
return [new Uint8Array(buf.buffer, 0, readBytes), false]; | ||
} | ||
catch (e) { | ||
_convertFsError(e); | ||
} | ||
break; | ||
} | ||
default: throw null; | ||
} | ||
} | ||
@@ -23,3 +56,4 @@ export function skip(s, _len) { | ||
export function dropInputStream(s) { | ||
console.log(`[streams] Drop input stream ${s}`); | ||
delete _streams[s]; | ||
} | ||
@@ -26,0 +60,0 @@ export function write(s, buf) { |
@@ -1,12 +0,9 @@ | ||
export function now(clock) { | ||
if (clock === 1) { | ||
const seconds = BigInt(Math.floor(Date.now() / 1000)); | ||
const nanoseconds = (Date.now() % 1000) * 1000 * 1000; | ||
return { seconds, nanoseconds }; | ||
} | ||
console.log(`[wall-clock] now() UNKNOWN CLOCK ${clock}`); | ||
export function now() { | ||
const seconds = BigInt(Math.floor(Date.now() / 1e3)); | ||
const nanoseconds = (Date.now() % 1e3) * 1e6; | ||
return { seconds, nanoseconds }; | ||
} | ||
export function resolution(clock) { | ||
console.log(`[wall-clock] Wall clock resolution ${clock}`); | ||
export function resolution() { | ||
console.log(`[wall-clock] Wall clock resolution`); | ||
} |
{ | ||
"name": "@bytecodealliance/preview2-shim", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"description": "WASI Preview2 shim for JS environments", | ||
@@ -5,0 +5,0 @@ "author": "Guy Bedford, Eduardo Rodrigues<16357187+eduardomourar@users.noreply.github.com>", |
@@ -11,6 +11,6 @@ # WASI Preview2 JavaScript Shim | ||
| --------------- | ----------------------------:|-----------------------------:| | ||
| Clocks | Pending timezone, resolution | Pending timezone, resolution | | ||
| Filesystem | :x: | :x: | | ||
| Clocks | Pending timezone, poll | Pending timezone, poll | | ||
| Filesystem | Basic read support | :x: | | ||
| HTTP | :x: | :x: | | ||
| IO | :x: | :x: | | ||
| IO | Basic FS Streams | :x: | | ||
| Logging | :heavy_check_mark: | :heavy_check_mark: | | ||
@@ -17,0 +17,0 @@ | Poll | :x: | :x: | |
@@ -9,3 +9,2 @@ export namespace Filesystem { | ||
export function getType(this: Descriptor): DescriptorType; | ||
export function setFlags(this: Descriptor, flags: DescriptorFlags): void; | ||
export function setSize(this: Descriptor, size: Filesize): void; | ||
@@ -142,3 +141,2 @@ export function setTimes(this: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; | ||
write?: boolean, | ||
nonBlocking?: boolean, | ||
fileIntegritySync?: boolean, | ||
@@ -145,0 +143,0 @@ dataIntegritySync?: boolean, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
87216
79
2668
1