| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.Scope = exports.WeakLifetime = exports.StaticLifetime = exports.Lifetime = void 0; |
| const asyncify_helpers_1 = require("./asyncify-helpers"); |
| const debug_1 = require("./debug"); |
| const errors_1 = require("./errors"); |
| /** |
| * A lifetime prevents access to a value after the lifetime has been |
| * [[dispose]]ed. |
| * |
| * Typically, quickjs-emscripten uses Lifetimes to protect C memory pointers. |
| */ |
| class Lifetime { |
| /** |
| * When the Lifetime is disposed, it will call `disposer(_value)`. Use the |
| * disposer function to implement whatever cleanup needs to happen at the end |
| * of `value`'s lifetime. |
| * |
| * `_owner` is not used or controlled by the lifetime. It's just metadata for |
| * the creator. |
| */ |
| constructor(_value, copier, disposer, _owner) { |
| this._value = _value; |
| this.copier = copier; |
| this.disposer = disposer; |
| this._owner = _owner; |
| this._alive = true; |
| this._constructorStack = debug_1.QTS_DEBUG ? new Error("Lifetime constructed").stack : undefined; |
| } |
| get alive() { |
| return this._alive; |
| } |
| /** |
| * The value this Lifetime protects. You must never retain the value - it |
| * may become invalid, leading to memory errors. |
| * |
| * @throws If the lifetime has been [[dispose]]d already. |
| */ |
| get value() { |
| this.assertAlive(); |
| return this._value; |
| } |
| get owner() { |
| return this._owner; |
| } |
| get dupable() { |
| return !!this.copier; |
| } |
| /** |
| * Create a new handle pointing to the same [[value]]. |
| */ |
| dup() { |
| this.assertAlive(); |
| if (!this.copier) { |
| throw new Error("Non-dupable lifetime"); |
| } |
| return new Lifetime(this.copier(this._value), this.copier, this.disposer, this._owner); |
| } |
| consume(map) { |
| this.assertAlive(); |
| const result = map(this); |
| this.dispose(); |
| return result; |
| } |
| /** |
| * Dispose of [[value]] and perform cleanup. |
| */ |
| dispose() { |
| this.assertAlive(); |
| if (this.disposer) { |
| this.disposer(this._value); |
| } |
| this._alive = false; |
| } |
| assertAlive() { |
| if (!this.alive) { |
| if (this._constructorStack) { |
| throw new errors_1.QuickJSUseAfterFree(`Lifetime not alive\n${this._constructorStack}\nLifetime used`); |
| } |
| throw new errors_1.QuickJSUseAfterFree("Lifetime not alive"); |
| } |
| } |
| } |
| exports.Lifetime = Lifetime; |
| /** |
| * A Lifetime that lives forever. Used for constants. |
| */ |
| class StaticLifetime extends Lifetime { |
| constructor(value, owner) { |
| super(value, undefined, undefined, owner); |
| } |
| // Static lifetime doesn't need a copier to be copiable |
| get dupable() { |
| return true; |
| } |
| // Copy returns the same instance. |
| dup() { |
| return this; |
| } |
| // Dispose does nothing. |
| dispose() { } |
| } |
| exports.StaticLifetime = StaticLifetime; |
| /** |
| * A Lifetime that does not own its `value`. A WeakLifetime never calls its |
| * `disposer` function, but can be `dup`ed to produce regular lifetimes that |
| * do. |
| * |
| * Used for function arguments. |
| */ |
| class WeakLifetime extends Lifetime { |
| constructor(value, copier, disposer, owner) { |
| // We don't care if the disposer doesn't support freeing T |
| super(value, copier, disposer, owner); |
| } |
| dispose() { |
| this._alive = false; |
| } |
| } |
| exports.WeakLifetime = WeakLifetime; |
| function scopeFinally(scope, blockError) { |
| // console.log('scopeFinally', scope, blockError) |
| let disposeError; |
| try { |
| scope.dispose(); |
| } |
| catch (error) { |
| disposeError = error; |
| } |
| if (blockError && disposeError) { |
| Object.assign(blockError, { |
| message: `${blockError.message}\n Then, failed to dispose scope: ${disposeError.message}`, |
| disposeError, |
| }); |
| throw blockError; |
| } |
| if (blockError || disposeError) { |
| throw blockError || disposeError; |
| } |
| } |
| /** |
| * Scope helps reduce the burden of manually tracking and disposing of |
| * Lifetimes. See [[withScope]]. and [[withScopeAsync]]. |
| */ |
| class Scope { |
| constructor() { |
| this._disposables = new Lifetime(new Set()); |
| } |
| /** |
| * Run `block` with a new Scope instance that will be disposed after the block returns. |
| * Inside `block`, call `scope.manage` on each lifetime you create to have the lifetime |
| * automatically disposed after the block returns. |
| * |
| * @warning Do not use with async functions. Instead, use [[withScopeAsync]]. |
| */ |
| static withScope(block) { |
| const scope = new Scope(); |
| let blockError; |
| try { |
| return block(scope); |
| } |
| catch (error) { |
| blockError = error; |
| throw error; |
| } |
| finally { |
| scopeFinally(scope, blockError); |
| } |
| } |
| static withScopeMaybeAsync(_this, block) { |
| return (0, asyncify_helpers_1.maybeAsync)(undefined, function* (awaited) { |
| const scope = new Scope(); |
| let blockError; |
| try { |
| return yield* awaited.of(block.call(_this, awaited, scope)); |
| } |
| catch (error) { |
| blockError = error; |
| throw error; |
| } |
| finally { |
| scopeFinally(scope, blockError); |
| } |
| }); |
| } |
| /** |
| * Run `block` with a new Scope instance that will be disposed after the |
| * block's returned promise settles. Inside `block`, call `scope.manage` on each |
| * lifetime you create to have the lifetime automatically disposed after the |
| * block returns. |
| */ |
| static async withScopeAsync(block) { |
| const scope = new Scope(); |
| let blockError; |
| try { |
| return await block(scope); |
| } |
| catch (error) { |
| blockError = error; |
| throw error; |
| } |
| finally { |
| scopeFinally(scope, blockError); |
| } |
| } |
| /** |
| * Track `lifetime` so that it is disposed when this scope is disposed. |
| */ |
| manage(lifetime) { |
| this._disposables.value.add(lifetime); |
| return lifetime; |
| } |
| get alive() { |
| return this._disposables.alive; |
| } |
| dispose() { |
| const lifetimes = Array.from(this._disposables.value.values()).reverse(); |
| for (const lifetime of lifetimes) { |
| if (lifetime.alive) { |
| lifetime.dispose(); |
| } |
| } |
| this._disposables.dispose(); |
| } |
| } |
| exports.Scope = Scope; |
| //# sourceMappingURL=lifetime.js.map |