hakojs
Version:
A secure, embeddable JavaScript engine that runs untrusted code inside WebAssembly sandboxes with fine-grained permissions and resource limits
283 lines (280 loc) • 10.4 kB
JavaScript
var __dispose = Symbol.dispose || /* @__PURE__ */ Symbol.for("Symbol.dispose");
var __asyncDispose = Symbol.dispose || /* @__PURE__ */ Symbol.for("Symbol.dispose");
var __using = (stack, value, async) => {
if (value != null) {
if (typeof value !== "object" && typeof value !== "function")
throw TypeError('Object expected to be assigned to "using" declaration');
var dispose;
if (async)
dispose = value[__asyncDispose];
if (dispose === undefined)
dispose = value[__dispose];
if (typeof dispose !== "function")
throw TypeError("Object not disposable");
stack.push([async, dispose, value]);
} else if (async) {
stack.push([async]);
}
return value;
};
var __callDispose = (stack, error, hasError) => {
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
}, fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e), next = (it) => {
while (it = stack.pop()) {
try {
var result = it[1] && it[1].call(it[2]);
if (it[0])
return Promise.resolve(result).then(next, (e) => (fail(e), next()));
} catch (e) {
fail(e);
}
}
if (hasError)
throw error;
};
return next();
};
// src/vm/value-factory.ts
import { HakoError } from "../etc/errors";
import {
detectCircularReferences
} from "../etc/types";
import { VMValue } from "./value";
class ValueFactory {
context;
container;
constructor(context, container) {
this.context = context;
this.container = container;
}
getContext() {
return this.context;
}
fromNativeValue(value, options = {}) {
if (value === undefined) {
return this.createUndefined();
}
if (value === null) {
return this.createNull();
}
if (typeof value === "boolean") {
return this.createBoolean(value);
}
if (typeof value === "number") {
return this.createNumber(value);
}
if (typeof value === "string") {
return this.createString(value);
}
if (typeof value === "bigint") {
return this.createBigInt(value);
}
if (typeof value === "symbol") {
return this.createSymbol(value, options);
}
if (typeof value === "function") {
return this.createFunction(value, options);
}
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
return this.createArrayBuffer(value);
}
if (Array.isArray(value)) {
return this.createArray(value);
}
if (value instanceof Date) {
return this.createDate(value);
}
if (value instanceof Error) {
return this.createError(value);
}
if (typeof value === "object") {
return this.createObject(value, options);
}
throw new Error("Unsupported value type");
}
createError(value) {
let __stack2 = [];
try {
const errorPtr = this.container.exports.HAKO_NewError(this.context.pointer);
const message = __using(__stack2, this.createString(value.message), 0);
const name = __using(__stack2, this.createString(value.name), 0);
const stack = __using(__stack2, this.createString(value.stack || ""), 0);
const extractCause = (err) => {
if (err.cause !== undefined) {
return err.cause;
}
const errWithOptions = err;
return errWithOptions.options?.cause;
};
const causeValue = extractCause(value);
const cause = __using(__stack2, causeValue !== undefined ? this.fromNativeValue(causeValue) : null, 0);
const messageKey = __using(__stack2, this.createString("message"), 0);
this.container.exports.HAKO_SetProp(this.context.pointer, errorPtr, messageKey.getHandle(), message.getHandle());
const nameKey = __using(__stack2, this.createString("name"), 0);
this.container.exports.HAKO_SetProp(this.context.pointer, errorPtr, nameKey.getHandle(), name.getHandle());
if (cause) {
let __stack = [];
try {
const causeKey = __using(__stack, this.createString("cause"), 0);
this.container.exports.HAKO_SetProp(this.context.pointer, errorPtr, causeKey.getHandle(), cause.getHandle());
} catch (_catch) {
var _err = _catch, _hasErr = 1;
} finally {
__callDispose(__stack, _err, _hasErr);
}
}
const stackKey = __using(__stack2, this.createString("stack"), 0);
this.container.exports.HAKO_SetProp(this.context.pointer, errorPtr, stackKey.getHandle(), stack.getHandle());
return new VMValue(this.context, errorPtr, "owned");
} catch (_catch2) {
var _err2 = _catch2, _hasErr2 = 1;
} finally {
__callDispose(__stack2, _err2, _hasErr2);
}
}
createFunction(callback, options) {
if (!options.name || typeof options.name !== "string") {
throw new Error("Function name is required");
}
const functionId = this.container.callbacks.newFunction(this.context.pointer, callback, options.name);
return new VMValue(this.context, functionId, "owned");
}
createUndefined() {
return new VMValue(this.context, this.container.exports.HAKO_GetUndefined(), "borrowed");
}
createNull() {
return new VMValue(this.context, this.container.exports.HAKO_GetNull(), "borrowed");
}
createBoolean(value) {
if (value === true) {
return new VMValue(this.context, this.container.exports.HAKO_GetTrue(), "borrowed");
}
return new VMValue(this.context, this.container.exports.HAKO_GetFalse(), "borrowed");
}
createNumber(value) {
const numPtr = this.container.exports.HAKO_NewFloat64(this.context.pointer, value);
return new VMValue(this.context, numPtr, "owned");
}
createString(value) {
const strPtr = this.container.memory.allocateString(this.context.pointer, value);
const jsStrPtr = this.container.exports.HAKO_NewString(this.context.pointer, strPtr);
this.container.memory.freeMemory(this.context.pointer, strPtr);
return new VMValue(this.context, jsStrPtr, "owned");
}
createBigInt(value) {
if (!this.context.container.utils.getBuildInfo().hasBignum) {
throw new HakoError("This build of Hako does not have BigInt enabled.");
}
const isNegative = value < 0n;
const absValue = isNegative ? -value : value;
const low = Number(absValue & 0xffffffffn);
const high = Number(absValue >> 32n);
let bigIntPtr;
if (isNegative) {
bigIntPtr = this.container.exports.HAKO_NewBigInt(this.context.pointer, low, high);
} else {
bigIntPtr = this.container.exports.HAKO_NewBigUInt(this.context.pointer, low, high);
}
const lastError = this.context.getLastError(bigIntPtr);
if (lastError) {
this.container.memory.freeValuePointer(this.context.pointer, bigIntPtr);
throw lastError;
}
return new VMValue(this.context, bigIntPtr, "owned");
}
createSymbol(value, options) {
const isGlobal = options.isGlobal || false;
return this.createString(value.description || "").consume((value2) => {
const jsSymbolPtr = this.container.exports.HAKO_NewSymbol(this.context.pointer, value2.getHandle(), isGlobal ? 1 : 0);
return new VMValue(this.context, jsSymbolPtr, "owned");
});
}
createArray(value) {
const arrayPtr = this.container.exports.HAKO_NewArray(this.context.pointer);
const jsArray = new VMValue(this.context, arrayPtr, "owned");
for (let i = 0;i < value.length; i++) {
let __stack = [];
try {
const item = __using(__stack, this.fromNativeValue(value[i]), 0);
if (item) {
jsArray.setProperty(i, item);
}
} catch (_catch) {
var _err = _catch, _hasErr = 1;
} finally {
__callDispose(__stack, _err, _hasErr);
}
}
return jsArray;
}
createDate(value) {
const date = this.container.exports.HAKO_NewDate(this.context.pointer, value.getTime());
const error = this.context.getLastError(date);
if (error) {
this.container.memory.freeValuePointer(this.context.pointer, date);
throw error;
}
return new VMValue(this.context, date, "owned");
}
createArrayBuffer(value) {
let buffer;
if (value instanceof ArrayBuffer) {
buffer = new Uint8Array(value);
} else {
buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
}
const valuePtr = this.container.memory.newArrayBuffer(this.context.pointer, buffer);
const lastError = this.context.getLastError(valuePtr);
if (lastError) {
this.container.memory.freeValuePointer(this.context.pointer, valuePtr);
throw lastError;
}
return new VMValue(this.context, valuePtr, "owned");
}
createObject(value, options) {
let __stack2 = [];
try {
detectCircularReferences(value);
const objPtr = options.proto && options.proto instanceof VMValue ? this.container.exports.HAKO_NewObjectProto(this.context.pointer, options.proto.getHandle()) : this.container.exports.HAKO_NewObject(this.context.pointer);
const lastError = this.context.getLastError(objPtr);
if (lastError) {
this.container.memory.freeValuePointer(this.context.pointer, objPtr);
throw lastError;
}
const jsObj = __using(__stack2, new VMValue(this.context, objPtr, "owned"), 0);
for (const key in value) {
if (Object.hasOwn(value, key)) {
let __stack = [];
try {
const propValue = __using(__stack, this.fromNativeValue(value[key]), 0);
if (propValue) {
jsObj.setProperty(key, propValue);
}
} catch (_catch) {
var _err = _catch, _hasErr = 1;
} finally {
__callDispose(__stack, _err, _hasErr);
}
}
}
return jsObj.dup();
} catch (_catch2) {
var _err2 = _catch2, _hasErr2 = 1;
} finally {
__callDispose(__stack2, _err2, _hasErr2);
}
}
getGlobalObject() {
return new VMValue(this.context, this.container.exports.HAKO_GetGlobalObject(this.context.pointer), "owned");
}
dispose() {}
[Symbol.dispose]() {
this.dispose();
}
}
export {
ValueFactory
};
//# debugId=16C559CC6349F8A664756E2164756E21
//# sourceMappingURL=value-factory.js.map