hakojs
Version:
A secure, embeddable JavaScript engine that runs untrusted code inside WebAssembly sandboxes with fine-grained permissions and resource limits
150 lines (144 loc) • 3.23 kB
JavaScript
// src/mem/lifetime.ts
function isDisposable(value) {
return Boolean(value && typeof value === "object" && "dispose" in value && typeof value.dispose === "function" && "alive" in value && typeof value.alive === "boolean");
}
function isAbstractDisposableResult(value) {
return Boolean(value && typeof value === "object" && value instanceof AbstractDisposableResult);
}
class AbstractDisposableResult {
static success(value) {
return new DisposableSuccess(value);
}
static fail(error, onUnwrap) {
return new DisposableFail(error, onUnwrap);
}
static is(result) {
return result instanceof AbstractDisposableResult;
}
[Symbol.dispose]() {
this.dispose();
}
}
class DisposableSuccess extends AbstractDisposableResult {
value;
constructor(value) {
super();
this.value = value;
}
get alive() {
return isDisposable(this.value) ? this.value.alive : true;
}
dispose() {
if (isDisposable(this.value)) {
this.value.dispose();
}
}
unwrap() {
return this.value;
}
unwrapOr(_fallback) {
return this.value;
}
}
class DisposableFail extends AbstractDisposableResult {
error;
onUnwrap;
constructor(error, onUnwrap) {
super();
this.error = error;
this.onUnwrap = onUnwrap;
}
get alive() {
return isDisposable(this.error) ? this.error.alive : true;
}
dispose() {
if (isDisposable(this.error)) {
this.error.dispose();
}
}
unwrap() {
this.onUnwrap(this);
throw this.error;
}
unwrapOr(fallback) {
return fallback;
}
}
var DisposableResult = AbstractDisposableResult;
function scopeFinally(scope, blockError) {
let disposeError;
try {
scope.release();
} catch (error) {
disposeError = error;
}
if (blockError && disposeError) {
Object.assign(blockError, {
message: `${blockError.message}
Then, failed to dispose scope: ${disposeError.message}`,
disposeError
});
throw blockError;
}
if (blockError || disposeError) {
throw blockError || disposeError;
}
}
class Scope {
cleanupFns = [];
isDisposed = false;
add(fn) {
if (this.isDisposed) {
throw new Error("Cannot add cleanup function to a disposed scope");
}
this.cleanupFns.push(fn);
}
release() {
if (this.isDisposed) {
return;
}
for (let i = this.cleanupFns.length - 1;i >= 0; i--) {
try {
this.cleanupFns[i]();
} catch (error) {
console.error("Error during cleanup:", error);
}
}
this.cleanupFns = [];
this.isDisposed = true;
}
manage(value) {
if (isDisposable(value)) {
this.add(() => {
if (value.alive) {
value.dispose();
}
});
}
return value;
}
static withScope(block) {
const scope = new Scope;
let blockError;
try {
return block(scope);
} catch (error) {
blockError = error;
throw error;
} finally {
scopeFinally(scope, blockError);
}
}
[Symbol.dispose]() {
this.release();
}
}
export {
isAbstractDisposableResult,
Scope,
DisposableSuccess,
DisposableResult,
DisposableFail
};
//# debugId=AE3DC613B1E704FA64756E2164756E21
//# sourceMappingURL=lifetime.js.map