Spaces:
Sleeping
Sleeping
File size: 6,724 Bytes
c2b7eb3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | "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 |