2226 lines
69 KiB
JavaScript
2226 lines
69 KiB
JavaScript
//#region src/util.ts
|
|
function detectFinalizer() {
|
|
return (typeof FinalizationRegistry !== 'undefined') && (typeof WeakRef !== 'undefined');
|
|
}
|
|
const supportFinalizer = detectFinalizer();
|
|
function detectFeatures(features) {
|
|
const supportNewFunction = (function () {
|
|
let f;
|
|
try {
|
|
f = new Function();
|
|
}
|
|
catch (_) {
|
|
return false;
|
|
}
|
|
return typeof f === 'function';
|
|
})();
|
|
const canSetFunctionName = (function () {
|
|
try {
|
|
return Boolean(Object.getOwnPropertyDescriptor(Function.prototype, 'name')?.configurable);
|
|
}
|
|
catch (_) {
|
|
return false;
|
|
}
|
|
})();
|
|
const weakSymbol = (function () {
|
|
try {
|
|
const sym = Symbol();
|
|
new WeakRef(sym);
|
|
new WeakMap().set(sym, undefined);
|
|
}
|
|
catch (_) {
|
|
return false;
|
|
}
|
|
return true;
|
|
})();
|
|
const _require = (function () {
|
|
let nativeRequire;
|
|
if (typeof __webpack_public_path__ !== 'undefined') {
|
|
nativeRequire = (function () {
|
|
return typeof __non_webpack_require__ !== 'undefined' ? __non_webpack_require__ : undefined;
|
|
})();
|
|
}
|
|
else {
|
|
nativeRequire = (function () {
|
|
return typeof __webpack_public_path__ !== 'undefined' ? (typeof __non_webpack_require__ !== 'undefined' ? __non_webpack_require__ : undefined) : (typeof require !== 'undefined' ? require : undefined);
|
|
})();
|
|
}
|
|
return nativeRequire;
|
|
})();
|
|
function defaultGetGlobalThis() {
|
|
if (typeof globalThis !== 'undefined')
|
|
return globalThis;
|
|
let g = (function () { return this; })();
|
|
if (!g && supportNewFunction) {
|
|
try {
|
|
g = new Function('return this')();
|
|
}
|
|
catch (_) { }
|
|
}
|
|
if (!g) {
|
|
if (typeof __webpack_public_path__ === 'undefined') {
|
|
if (typeof global !== 'undefined')
|
|
return global;
|
|
}
|
|
if (typeof window !== 'undefined')
|
|
return window;
|
|
if (typeof self !== 'undefined')
|
|
return self;
|
|
}
|
|
throw new Error('Unable to get globalThis');
|
|
}
|
|
function defaultWithResolvers() {
|
|
let ok;
|
|
let err;
|
|
const promise = new this((resolve, reject) => {
|
|
ok = resolve;
|
|
err = reject;
|
|
});
|
|
return { promise, resolve: ok, reject: err };
|
|
}
|
|
const getGlobalThis = features && 'getGlobalThis' in features
|
|
? (features.getGlobalThis ?? defaultGetGlobalThis)
|
|
: defaultGetGlobalThis;
|
|
const withResolvers = (features && 'withResolvers' in features)
|
|
? (features.withResolvers ?? defaultWithResolvers)
|
|
: typeof Promise.withResolvers === 'function'
|
|
? Promise.withResolvers
|
|
: defaultWithResolvers;
|
|
const getBuiltinModule = getGlobalThis?.().process?.getBuiltinModule ?? _require;
|
|
const ret = {
|
|
makeDynamicFunction: (supportNewFunction
|
|
? (...args) => (new Function(...args))
|
|
: undefined),
|
|
setFunctionName: (canSetFunctionName
|
|
? (f, name) => { Object.defineProperty(f, 'name', { value: name }); }
|
|
: undefined),
|
|
Reflect: typeof Reflect === 'object' ? Reflect : undefined,
|
|
finalizer: detectFinalizer(),
|
|
weakSymbol,
|
|
BigInt: typeof BigInt !== 'undefined' ? BigInt : undefined,
|
|
MessageChannel: typeof MessageChannel === 'function'
|
|
? MessageChannel
|
|
: (function () {
|
|
try {
|
|
return getBuiltinModule('worker_threads').MessageChannel;
|
|
}
|
|
catch (_) { }
|
|
return undefined;
|
|
})(),
|
|
setImmediate: typeof setImmediate === 'function'
|
|
? setImmediate.bind(getGlobalThis())
|
|
: function (callback) {
|
|
if (typeof callback !== 'function') {
|
|
throw new TypeError('The "callback" argument must be of type function');
|
|
}
|
|
if (ret.MessageChannel) {
|
|
let channel = new ret.MessageChannel();
|
|
channel.port1.onmessage = function () {
|
|
channel.port1.onmessage = null;
|
|
channel = undefined;
|
|
callback();
|
|
};
|
|
channel.port2.postMessage(null);
|
|
}
|
|
else {
|
|
setTimeout(callback, 0);
|
|
}
|
|
},
|
|
Buffer: typeof Buffer === 'function'
|
|
? Buffer
|
|
: (function () {
|
|
try {
|
|
return getBuiltinModule('buffer').Buffer;
|
|
}
|
|
catch (_) { }
|
|
return undefined;
|
|
})(),
|
|
...features,
|
|
getGlobalThis,
|
|
withResolvers
|
|
};
|
|
return ret;
|
|
}
|
|
function isReferenceType(v) {
|
|
return (typeof v === 'object' && v !== null) || typeof v === 'function';
|
|
}
|
|
function getDylinkMetadata(binary) {
|
|
let offset = 0;
|
|
let end = 0;
|
|
const decoder = new TextDecoder();
|
|
function getU8() {
|
|
return binary[offset++];
|
|
}
|
|
function getLEB() {
|
|
let ret = 0;
|
|
let mul = 1;
|
|
while (1) {
|
|
let byte = binary[offset++];
|
|
ret += ((byte & 0x7f) * mul);
|
|
mul *= 0x80;
|
|
if (!(byte & 0x80))
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
function getString() {
|
|
let len = getLEB();
|
|
offset += len;
|
|
return decoder.decode(binary.subarray(offset - len, offset));
|
|
}
|
|
function getStringList() {
|
|
let count = getLEB();
|
|
let rtn = [];
|
|
while (count--)
|
|
rtn.push(getString());
|
|
return rtn;
|
|
}
|
|
function failIf(condition, message) {
|
|
if (condition)
|
|
throw new Error(message);
|
|
}
|
|
if (binary instanceof WebAssembly.Module) {
|
|
const dylinkSection = WebAssembly.Module.customSections(binary, 'dylink.0');
|
|
failIf(dylinkSection.length === 0, 'need dylink section');
|
|
binary = new Uint8Array(dylinkSection[0]);
|
|
end = binary.length;
|
|
}
|
|
else {
|
|
var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer);
|
|
var magicNumberFound = int32View[0] === 0x6d736100 || int32View[0] === 0x0061736d;
|
|
failIf(!magicNumberFound, 'need to see wasm magic number');
|
|
failIf(binary[8] !== 0, 'need the dylink section to be first');
|
|
offset = 9;
|
|
var section_size = getLEB();
|
|
end = offset + section_size;
|
|
var name = getString();
|
|
failIf(name !== 'dylink.0');
|
|
}
|
|
var customSection = {
|
|
neededDynlibs: [],
|
|
tlsExports: new Set(),
|
|
weakImports: new Set(),
|
|
runtimePaths: [],
|
|
memorySize: 0,
|
|
memoryAlign: 0,
|
|
tableSize: 0,
|
|
tableAlign: 0
|
|
};
|
|
var WASM_DYLINK_MEM_INFO = 0x1;
|
|
var WASM_DYLINK_NEEDED = 0x2;
|
|
var WASM_DYLINK_EXPORT_INFO = 0x3;
|
|
var WASM_DYLINK_IMPORT_INFO = 0x4;
|
|
var WASM_DYLINK_RUNTIME_PATH = 0x5;
|
|
var WASM_SYMBOL_TLS = 0x100;
|
|
var WASM_SYMBOL_BINDING_MASK = 0x3;
|
|
var WASM_SYMBOL_BINDING_WEAK = 0x1;
|
|
while (offset < end) {
|
|
var subsectionType = getU8();
|
|
var subsectionSize = getLEB();
|
|
if (subsectionType === WASM_DYLINK_MEM_INFO) {
|
|
customSection.memorySize = getLEB();
|
|
customSection.memoryAlign = getLEB();
|
|
customSection.tableSize = getLEB();
|
|
customSection.tableAlign = getLEB();
|
|
}
|
|
else if (subsectionType === WASM_DYLINK_NEEDED) {
|
|
customSection.neededDynlibs = getStringList();
|
|
}
|
|
else if (subsectionType === WASM_DYLINK_EXPORT_INFO) {
|
|
let count = getLEB();
|
|
while (count--) {
|
|
let symname = getString();
|
|
let flags = getLEB();
|
|
if (flags & WASM_SYMBOL_TLS) {
|
|
customSection.tlsExports.add(symname);
|
|
}
|
|
}
|
|
}
|
|
else if (subsectionType === WASM_DYLINK_IMPORT_INFO) {
|
|
let count = getLEB();
|
|
while (count--) {
|
|
getString();
|
|
let symname = getString();
|
|
let flags = getLEB();
|
|
if ((flags & WASM_SYMBOL_BINDING_MASK) === WASM_SYMBOL_BINDING_WEAK) {
|
|
customSection.weakImports.add(symname);
|
|
}
|
|
}
|
|
}
|
|
else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) {
|
|
customSection.runtimePaths = getStringList();
|
|
}
|
|
else {
|
|
console.error('unknown dylink.0 subsection:', subsectionType);
|
|
offset += subsectionSize;
|
|
}
|
|
}
|
|
var tableAlign = Math.pow(2, customSection.tableAlign);
|
|
failIf(tableAlign !== 1, `invalid tableAlign ${tableAlign}`);
|
|
failIf(offset !== end);
|
|
return customSection;
|
|
}
|
|
const version = "2.0.0-alpha.3";
|
|
const NODE_API_SUPPORTED_VERSION_MIN = 1;
|
|
const NODE_API_SUPPORTED_VERSION_MAX = 10;
|
|
const NAPI_VERSION_EXPERIMENTAL = 2147483647;
|
|
const NODE_API_DEFAULT_MODULE_API_VERSION = 8;
|
|
const NODE_MODULE_VERSION = 127;
|
|
|
|
//#endregion src/util.ts
|
|
|
|
//#region src/Disaposable.ts
|
|
class Disposable {
|
|
}
|
|
if (typeof Symbol.dispose === 'symbol') {
|
|
Object.defineProperty(Disposable.prototype, Symbol.dispose, {
|
|
value() {
|
|
this.dispose();
|
|
},
|
|
writable: true,
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
|
|
//#endregion src/Disaposable.ts
|
|
|
|
//#region src/RefTracker.ts
|
|
class RefTracker extends Disposable {
|
|
constructor() {
|
|
super(...arguments);
|
|
this._next = null;
|
|
this._prev = null;
|
|
}
|
|
dispose() { }
|
|
finalize() { }
|
|
link(list) {
|
|
this._prev = list;
|
|
this._next = list._next;
|
|
if (this._next !== null) {
|
|
this._next._prev = this;
|
|
}
|
|
list._next = this;
|
|
}
|
|
unlink() {
|
|
if (this._prev !== null) {
|
|
this._prev._next = this._next;
|
|
}
|
|
if (this._next !== null) {
|
|
this._next._prev = this._prev;
|
|
}
|
|
this._prev = null;
|
|
this._next = null;
|
|
}
|
|
static finalizeAll(list) {
|
|
while (list._next !== null) {
|
|
list._next.finalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
//#endregion src/RefTracker.ts
|
|
|
|
//#region src/Finalizer.ts
|
|
class Finalizer {
|
|
constructor(envObject, _finalizeCallback = 0, _finalizeData = 0, _finalizeHint = 0) {
|
|
this.envObject = envObject;
|
|
this._finalizeCallback = _finalizeCallback;
|
|
this._finalizeData = _finalizeData;
|
|
this._finalizeHint = _finalizeHint;
|
|
this._makeDynCall_vppp = envObject.bridge.makeDynCall_vppp;
|
|
}
|
|
copy() {
|
|
const newFina = Object.create(Finalizer.prototype);
|
|
newFina.envObject = this.envObject;
|
|
newFina._makeDynCall_vppp = this._makeDynCall_vppp;
|
|
newFina._finalizeCallback = this._finalizeCallback;
|
|
newFina._finalizeData = this._finalizeData;
|
|
newFina._finalizeHint = this._finalizeHint;
|
|
return newFina;
|
|
}
|
|
move(target) {
|
|
target._finalizeCallback = this._finalizeCallback;
|
|
target._finalizeData = this._finalizeData;
|
|
target._finalizeHint = this._finalizeHint;
|
|
target.envObject = this.envObject;
|
|
target._makeDynCall_vppp = this._makeDynCall_vppp;
|
|
this._finalizeCallback = 0;
|
|
this._finalizeData = 0;
|
|
this._finalizeHint = 0;
|
|
this.dispose();
|
|
}
|
|
callback() { return this._finalizeCallback; }
|
|
data() { return this._finalizeData; }
|
|
hint() { return this._finalizeHint; }
|
|
resetEnv() {
|
|
this.envObject = undefined;
|
|
}
|
|
resetFinalizer() {
|
|
this._finalizeCallback = 0;
|
|
this._finalizeData = 0;
|
|
this._finalizeHint = 0;
|
|
}
|
|
callFinalizer() {
|
|
const finalize_callback = this._finalizeCallback;
|
|
const finalize_data = this._finalizeData;
|
|
const finalize_hint = this._finalizeHint;
|
|
this.resetFinalizer();
|
|
if (!finalize_callback)
|
|
return;
|
|
const fini = Number(finalize_callback);
|
|
if (!this.envObject) {
|
|
this._makeDynCall_vppp(fini)(0, finalize_data, finalize_hint);
|
|
}
|
|
else {
|
|
this.envObject.callFinalizer(fini, finalize_data, finalize_hint);
|
|
}
|
|
}
|
|
dispose() {
|
|
this.envObject = undefined;
|
|
this._makeDynCall_vppp = undefined;
|
|
}
|
|
}
|
|
|
|
//#endregion src/Finalizer.ts
|
|
|
|
//#region src/TrackedFinalizer.ts
|
|
class TrackedFinalizer extends RefTracker {
|
|
static create(envObject, finalize_callback, finalize_data, finalize_hint) {
|
|
const finalizer = new TrackedFinalizer(envObject, finalize_callback, finalize_data, finalize_hint);
|
|
finalizer.link(envObject.finalizing_reflist);
|
|
return finalizer;
|
|
}
|
|
constructor(envObject, finalize_callback, finalize_data, finalize_hint) {
|
|
super();
|
|
this._finalizer = new Finalizer(envObject, finalize_callback, finalize_data, finalize_hint);
|
|
}
|
|
data() {
|
|
return this._finalizer.data();
|
|
}
|
|
dispose() {
|
|
if (!this._finalizer)
|
|
return;
|
|
this.unlink();
|
|
this._finalizer.envObject.dequeueFinalizer(this);
|
|
this._finalizer.dispose();
|
|
this._finalizer = undefined;
|
|
super.dispose();
|
|
}
|
|
finalize() {
|
|
this.unlink();
|
|
let error;
|
|
let caught = false;
|
|
try {
|
|
this._finalizer.callFinalizer();
|
|
}
|
|
catch (err) {
|
|
caught = true;
|
|
error = err;
|
|
}
|
|
this.dispose();
|
|
if (caught) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
//#endregion src/TrackedFinalizer.ts
|
|
|
|
//#region src/Store.ts
|
|
class CountIdAllocator extends Disposable {
|
|
constructor(initialNext = 1) {
|
|
super();
|
|
this.next = initialNext;
|
|
}
|
|
aquire() {
|
|
return this.next++;
|
|
}
|
|
release(_) { }
|
|
dispose() { }
|
|
}
|
|
class CountIdReuseAllocator extends CountIdAllocator {
|
|
constructor() {
|
|
super(...arguments);
|
|
this._freeList = [];
|
|
}
|
|
aquire() {
|
|
if (this._freeList.length) {
|
|
return this._freeList.shift();
|
|
}
|
|
return super.aquire();
|
|
}
|
|
release(id) {
|
|
this._freeList.push(id);
|
|
}
|
|
dispose() {
|
|
this._freeList.length = 0;
|
|
super.dispose();
|
|
}
|
|
}
|
|
class BaseArrayStore extends Disposable {
|
|
constructor(initialCapacity = 1) {
|
|
super();
|
|
this._values = Array(initialCapacity);
|
|
}
|
|
assign(id, value) {
|
|
this._values[id] = value;
|
|
return value;
|
|
}
|
|
deref(id) {
|
|
return this._values[id];
|
|
}
|
|
alloc(factory, ...args) {
|
|
return factory(...args);
|
|
}
|
|
dealloc(id) {
|
|
this._values[id] = undefined;
|
|
}
|
|
dispose() {
|
|
this._values.forEach((_, i) => this.dealloc(i));
|
|
}
|
|
}
|
|
class ArrayStore extends BaseArrayStore {
|
|
constructor(initialCapacity = 4) {
|
|
super(initialCapacity);
|
|
this._allocator = new CountIdReuseAllocator(1);
|
|
}
|
|
insert(value) {
|
|
const id = this._allocator.aquire();
|
|
while (id >= this._values.length) {
|
|
const cap = this._values.length;
|
|
this._values.length = cap + (cap >> 1) + 16;
|
|
}
|
|
value.id = id;
|
|
this._values[id] = value;
|
|
}
|
|
alloc(factory, ...args) {
|
|
const value = factory(...args);
|
|
this.insert(value);
|
|
return value;
|
|
}
|
|
dealloc(id) {
|
|
this._allocator.release(id);
|
|
super.dealloc(id);
|
|
}
|
|
}
|
|
|
|
//#endregion src/Store.ts
|
|
|
|
//#region src/Persistent.ts
|
|
class PersistentStore extends ArrayStore {
|
|
constructor(initialCapacity = 4) {
|
|
super(initialCapacity);
|
|
this._recyleList = [];
|
|
}
|
|
markUnused(id) {
|
|
this._recyleList.push(id);
|
|
}
|
|
recycle() {
|
|
while (this._recyleList.length > 0) {
|
|
const id = this._recyleList.shift();
|
|
const persistent = this.deref(id);
|
|
persistent.dispose();
|
|
}
|
|
}
|
|
}
|
|
class StrongRef extends Disposable {
|
|
constructor(value) {
|
|
super();
|
|
this._value = value;
|
|
}
|
|
deref() {
|
|
return this._value;
|
|
}
|
|
dispose() {
|
|
this._value = undefined;
|
|
}
|
|
}
|
|
class Persistent extends Disposable {
|
|
constructor(isolate, ...args) {
|
|
super();
|
|
this.id = 0;
|
|
this._isolate = isolate;
|
|
isolate.insertRef(this);
|
|
this.setSlot(args.length === 0 ? undefined : new StrongRef(args[0]));
|
|
}
|
|
dispose() {
|
|
this.reset();
|
|
this.deleteSlot();
|
|
this._isolate.globalHandleStore.dealloc(this.id);
|
|
this._isolate = undefined;
|
|
this.id = 0;
|
|
}
|
|
copy() {
|
|
const target = new Persistent(this._isolate);
|
|
target._param = this._param;
|
|
target._callback = this._callback;
|
|
const ref = this.getSlot();
|
|
if (ref instanceof StrongRef) {
|
|
target.setSlot(new StrongRef(ref.deref()));
|
|
}
|
|
else if (ref instanceof WeakRef) {
|
|
const value = ref.deref();
|
|
if (value === undefined) {
|
|
target.setSlot(undefined);
|
|
}
|
|
else {
|
|
target.setSlot(new StrongRef(value));
|
|
target.setWeak(this._param, this._callback);
|
|
}
|
|
}
|
|
else {
|
|
target.setSlot(undefined);
|
|
}
|
|
return target;
|
|
}
|
|
move(to) {
|
|
if (this === to)
|
|
return;
|
|
to.reset();
|
|
to._param = this._param;
|
|
to._callback = this._callback;
|
|
to._isolate = this._isolate;
|
|
to.setSlot(this.getSlot());
|
|
this.reset();
|
|
this.setSlot(undefined);
|
|
}
|
|
slot() {
|
|
return 0x80000000 + this.id;
|
|
}
|
|
getSlot() {
|
|
return this._isolate.getRefSlotValue(this.slot());
|
|
}
|
|
setSlot(ref) {
|
|
this._isolate.setRefSlotValue(this.slot(), ref);
|
|
}
|
|
deleteSlot() {
|
|
this._isolate.deleteRefSlotValue(this.slot());
|
|
}
|
|
setWeak(param, callback) {
|
|
const ref = this.getSlot();
|
|
if (!supportFinalizer || ref === undefined || ref instanceof WeakRef)
|
|
return;
|
|
const value = ref.deref();
|
|
try {
|
|
Persistent._registry.register(value, this, this);
|
|
const weakRef = new WeakRef(value);
|
|
ref.dispose();
|
|
this.setSlot(weakRef);
|
|
this._param = param;
|
|
this._callback = callback;
|
|
}
|
|
catch (err) {
|
|
if (typeof value === 'symbol') ;
|
|
else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
clearWeak() {
|
|
const ref = this.getSlot();
|
|
if (!supportFinalizer || ref === undefined)
|
|
return;
|
|
if (ref instanceof WeakRef) {
|
|
try {
|
|
Persistent._registry.unregister(this);
|
|
}
|
|
catch (_) { }
|
|
this._param = undefined;
|
|
this._callback = undefined;
|
|
const value = ref.deref();
|
|
if (value === undefined) {
|
|
this.setSlot(value);
|
|
}
|
|
else {
|
|
this.setSlot(new StrongRef(value));
|
|
}
|
|
}
|
|
}
|
|
reset() {
|
|
if (supportFinalizer) {
|
|
try {
|
|
Persistent._registry.unregister(this);
|
|
}
|
|
catch (_) { }
|
|
}
|
|
this._param = undefined;
|
|
this._callback = undefined;
|
|
const ref = this.getSlot();
|
|
if (ref instanceof StrongRef) {
|
|
ref.dispose();
|
|
}
|
|
this.setSlot(undefined);
|
|
}
|
|
resetTo(value) {
|
|
this.reset();
|
|
this.setSlot(new StrongRef(value));
|
|
}
|
|
isEmpty() {
|
|
return this.getSlot() === undefined;
|
|
}
|
|
deref() {
|
|
const ref = this.getSlot();
|
|
if (ref === undefined)
|
|
return undefined;
|
|
return ref.deref();
|
|
}
|
|
}
|
|
Persistent._registry = supportFinalizer
|
|
? new FinalizationRegistry((value) => {
|
|
value.setSlot(undefined);
|
|
const callback = value._callback;
|
|
const param = value._param;
|
|
value._callback = undefined;
|
|
value._param = undefined;
|
|
if (typeof callback === 'function') {
|
|
callback(param);
|
|
}
|
|
})
|
|
: undefined;
|
|
|
|
//#endregion src/Persistent.ts
|
|
|
|
//#region src/env.ts
|
|
function handleThrow(envObject, value) {
|
|
if (envObject.terminatedOrTerminating()) {
|
|
return;
|
|
}
|
|
throw value;
|
|
}
|
|
class Env extends Disposable {
|
|
constructor(ctx, store, bridge) {
|
|
super();
|
|
this.openHandleScopes = 0;
|
|
this.instanceData = null;
|
|
this.refs = 1;
|
|
this.reflist = new RefTracker();
|
|
this.finalizing_reflist = new RefTracker();
|
|
this.pendingFinalizers = [];
|
|
this.inGcFinalizer = false;
|
|
this._bindingMap = new WeakMap();
|
|
this.ctx = ctx;
|
|
this.store = store;
|
|
this.id = 0;
|
|
this.store.insert(this);
|
|
this.lastException = new Persistent(ctx.isolate);
|
|
this.bridge = bridge;
|
|
}
|
|
canCallIntoJs() {
|
|
return true;
|
|
}
|
|
terminatedOrTerminating() {
|
|
return !this.canCallIntoJs();
|
|
}
|
|
ref() {
|
|
this.refs++;
|
|
}
|
|
unref() {
|
|
this.refs--;
|
|
if (this.refs === 0) {
|
|
this.dispose();
|
|
}
|
|
}
|
|
clearLastError() {
|
|
this.bridge.setLastError(this.bridge.address, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
setLastError(error_code, engine_error_code = 0, engine_reserved = 0) {
|
|
this.bridge.setLastError(this.bridge.address, error_code, engine_error_code, engine_reserved);
|
|
return error_code;
|
|
}
|
|
callIntoModule(fn, handleException = handleThrow) {
|
|
const openHandleScopesBefore = this.openHandleScopes;
|
|
this.clearLastError();
|
|
const r = fn(this);
|
|
if (openHandleScopesBefore !== this.openHandleScopes) {
|
|
this.bridge.abort('open_handle_scopes != open_handle_scopes_before');
|
|
}
|
|
if (!this.lastException.isEmpty()) {
|
|
const err = this.lastException.deref();
|
|
this.lastException.reset();
|
|
handleException(this, err);
|
|
}
|
|
return r;
|
|
}
|
|
invokeFinalizerFromGC(finalizer) {
|
|
if (this.moduleApiVersion !== NAPI_VERSION_EXPERIMENTAL) {
|
|
this.enqueueFinalizer(finalizer);
|
|
}
|
|
else {
|
|
const saved = this.inGcFinalizer;
|
|
this.inGcFinalizer = true;
|
|
try {
|
|
finalizer.finalize();
|
|
}
|
|
finally {
|
|
this.inGcFinalizer = saved;
|
|
}
|
|
}
|
|
}
|
|
checkGCAccess() {
|
|
if (this.moduleApiVersion === NAPI_VERSION_EXPERIMENTAL && this.inGcFinalizer) {
|
|
this.bridge.abort('Finalizer is calling a function that may affect GC state.\n' +
|
|
'The finalizers are run directly from GC and must not affect GC ' +
|
|
'state.\n' +
|
|
'Use `node_api_post_finalizer` from inside of the finalizer to work ' +
|
|
'around this issue.\n' +
|
|
'It schedules the call as a new task in the event loop.');
|
|
}
|
|
}
|
|
enqueueFinalizer(finalizer) {
|
|
if (this.pendingFinalizers.indexOf(finalizer) === -1) {
|
|
this.pendingFinalizers.push(finalizer);
|
|
}
|
|
}
|
|
dequeueFinalizer(finalizer) {
|
|
const index = this.pendingFinalizers.indexOf(finalizer);
|
|
if (index !== -1) {
|
|
this.pendingFinalizers.splice(index, 1);
|
|
}
|
|
}
|
|
deleteMe() {
|
|
RefTracker.finalizeAll(this.finalizing_reflist);
|
|
RefTracker.finalizeAll(this.reflist);
|
|
this.lastException.reset();
|
|
this.store.dealloc(this.id);
|
|
this.bridge.deleteEnv(this.bridge.address);
|
|
}
|
|
dispose() {
|
|
if (this.id === 0)
|
|
return;
|
|
this.deleteMe();
|
|
this.finalizing_reflist.dispose();
|
|
this.reflist.dispose();
|
|
this.id = 0;
|
|
}
|
|
initObjectBinding(value) {
|
|
const binding = {
|
|
wrapped: 0,
|
|
tag: null
|
|
};
|
|
this._bindingMap.set(value, binding);
|
|
return binding;
|
|
}
|
|
getObjectBinding(value) {
|
|
if (this._bindingMap.has(value)) {
|
|
return this._bindingMap.get(value);
|
|
}
|
|
return this.initObjectBinding(value);
|
|
}
|
|
setInstanceData(data, finalize_cb, finalize_hint) {
|
|
if (this.instanceData) {
|
|
this.instanceData.dispose();
|
|
}
|
|
this.instanceData = TrackedFinalizer.create(this, finalize_cb, data, finalize_hint);
|
|
}
|
|
getInstanceData() {
|
|
return this.instanceData ? this.instanceData.data() : 0;
|
|
}
|
|
}
|
|
class NodeEnv extends Env {
|
|
constructor(ctx, store, bridge) {
|
|
super(ctx, store, bridge);
|
|
this.destructing = false;
|
|
this.finalizationScheduled = false;
|
|
this.bridge = bridge;
|
|
}
|
|
deleteMe() {
|
|
this.destructing = true;
|
|
this.drainFinalizerQueue();
|
|
super.deleteMe();
|
|
}
|
|
canCallIntoJs() {
|
|
return super.canCallIntoJs() && this.ctx.canCallIntoJs();
|
|
}
|
|
triggerFatalException(err) {
|
|
if (this.nodeBinding) {
|
|
this.nodeBinding.napi.fatalException(err);
|
|
}
|
|
else {
|
|
if (typeof process === 'object' && process !== null && typeof process._fatalException === 'function') {
|
|
const handled = process._fatalException(err);
|
|
if (!handled) {
|
|
console.error(err);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
callbackIntoModule(enforceUncaughtExceptionPolicy, fn) {
|
|
return this.callIntoModule(fn, (envObject, err) => {
|
|
if (envObject.terminatedOrTerminating()) {
|
|
return;
|
|
}
|
|
const hasProcess = typeof process === 'object' && process !== null;
|
|
const hasForceFlag = hasProcess ? Boolean(process.execArgv && (process.execArgv.indexOf('--force-node-api-uncaught-exceptions-policy') !== -1)) : false;
|
|
if (envObject.moduleApiVersion < 10 && !hasForceFlag && !enforceUncaughtExceptionPolicy) {
|
|
const warn = hasProcess && typeof process.emitWarning === 'function'
|
|
? process.emitWarning
|
|
: function (warning, type, code) {
|
|
if (warning instanceof Error) {
|
|
console.warn(warning.toString());
|
|
}
|
|
else {
|
|
const prefix = code ? `[${code}] ` : '';
|
|
console.warn(`${prefix}${type || 'Warning'}: ${warning}`);
|
|
}
|
|
};
|
|
warn('Uncaught Node-API callback exception detected, please run node with option --force-node-api-uncaught-exceptions-policy=true to handle those exceptions properly.', 'DeprecationWarning', 'DEP0168');
|
|
return;
|
|
}
|
|
envObject.triggerFatalException(err);
|
|
});
|
|
}
|
|
callFinalizer(cb, data, hint) {
|
|
this.callFinalizerInternal(1, cb, data, hint);
|
|
}
|
|
callFinalizerInternal(forceUncaught, cb, data, hint) {
|
|
const f = this.bridge.makeDynCall_vppp(cb);
|
|
const env = this.bridge.address;
|
|
const scope = this.ctx.openScope(this);
|
|
try {
|
|
this.callbackIntoModule(Boolean(forceUncaught), () => { f(env, data, hint); });
|
|
}
|
|
finally {
|
|
this.ctx.closeScope(this, scope);
|
|
}
|
|
}
|
|
enqueueFinalizer(finalizer) {
|
|
super.enqueueFinalizer(finalizer);
|
|
if (!this.finalizationScheduled && !this.destructing) {
|
|
this.finalizationScheduled = true;
|
|
this.ref();
|
|
this.ctx.features.setImmediate(() => {
|
|
this.finalizationScheduled = false;
|
|
this.unref();
|
|
this.drainFinalizerQueue();
|
|
});
|
|
}
|
|
}
|
|
drainFinalizerQueue() {
|
|
while (this.pendingFinalizers.length > 0) {
|
|
const refTracker = this.pendingFinalizers.shift();
|
|
refTracker.finalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
//#endregion src/env.ts
|
|
|
|
//#region src/errors.ts
|
|
class EmnapiError extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
const ErrorConstructor = new.target;
|
|
const proto = ErrorConstructor.prototype;
|
|
if (!(this instanceof EmnapiError)) {
|
|
const setPrototypeOf = Object.setPrototypeOf;
|
|
if (typeof setPrototypeOf === 'function') {
|
|
setPrototypeOf.call(Object, this, proto);
|
|
}
|
|
else {
|
|
this.__proto__ = proto;
|
|
}
|
|
if (typeof Error.captureStackTrace === 'function') {
|
|
Error.captureStackTrace(this, ErrorConstructor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Object.defineProperty(EmnapiError.prototype, 'name', {
|
|
configurable: true,
|
|
writable: true,
|
|
value: 'EmnapiError'
|
|
});
|
|
class NotSupportWeakRefError extends EmnapiError {
|
|
constructor(api, message) {
|
|
super(`${api}: The current runtime does not support "FinalizationRegistry" and "WeakRef".${message ? ` ${message}` : ''}`);
|
|
}
|
|
}
|
|
Object.defineProperty(NotSupportWeakRefError.prototype, 'name', {
|
|
configurable: true,
|
|
writable: true,
|
|
value: 'NotSupportWeakRefError'
|
|
});
|
|
class NotSupportBufferError extends EmnapiError {
|
|
constructor(api, message) {
|
|
super(`${api}: The current runtime does not support "Buffer". Consider using buffer polyfill to make sure \`globalThis.Buffer\` is defined.${message ? ` ${message}` : ''}`);
|
|
}
|
|
}
|
|
Object.defineProperty(NotSupportBufferError.prototype, 'name', {
|
|
configurable: true,
|
|
writable: true,
|
|
value: 'NotSupportBufferError'
|
|
});
|
|
|
|
//#endregion src/errors.ts
|
|
|
|
//#region src/Reference.ts
|
|
var ReferenceOwnership;
|
|
(function (ReferenceOwnership) {
|
|
ReferenceOwnership[ReferenceOwnership["kRuntime"] = 0] = "kRuntime";
|
|
ReferenceOwnership[ReferenceOwnership["kUserland"] = 1] = "kUserland";
|
|
})(ReferenceOwnership || (ReferenceOwnership = {}));
|
|
function canBeHeldWeakly(value) {
|
|
if (!value)
|
|
return false;
|
|
const type = typeof value;
|
|
return type === 'object' || type === 'function' || type === 'symbol';
|
|
}
|
|
class Reference extends RefTracker {
|
|
static weakCallback(ref) {
|
|
const persistent = ref.getPersistent();
|
|
persistent.reset();
|
|
ref.invokeFinalizerFromGC();
|
|
}
|
|
static create(ctx, envObject, handle_id, initialRefcount, ownership, _unused1, _unused2, _unused3) {
|
|
const ref = new Reference(ctx, envObject, handle_id, initialRefcount, ownership);
|
|
if (envObject) {
|
|
ref.link(envObject.reflist);
|
|
}
|
|
return ref;
|
|
}
|
|
constructor(ctx, envObject, handle_id, initialRefcount, ownership) {
|
|
super();
|
|
this.envObject = envObject;
|
|
this._refcount = initialRefcount;
|
|
this._ownership = ownership;
|
|
const value = envObject ? envObject.ctx.jsValueFromNapiValue(handle_id) : handle_id;
|
|
this.canBeWeak = canBeHeldWeakly(value);
|
|
this.ctx = ctx;
|
|
this.id = new Persistent(ctx.isolate, value).id;
|
|
ctx.refStore.set(this.id, this);
|
|
if (initialRefcount === 0) {
|
|
this._setWeak();
|
|
}
|
|
}
|
|
getPersistent() {
|
|
return this.ctx.isolate.getRef(this.id);
|
|
}
|
|
ref() {
|
|
const persistent = this.getPersistent();
|
|
if (persistent.isEmpty()) {
|
|
return 0;
|
|
}
|
|
if (++this._refcount === 1 && this.canBeWeak) {
|
|
persistent.clearWeak();
|
|
}
|
|
return this._refcount;
|
|
}
|
|
unref() {
|
|
const persistent = this.getPersistent();
|
|
if (persistent.isEmpty() || this._refcount === 0) {
|
|
return 0;
|
|
}
|
|
if (--this._refcount === 0) {
|
|
this._setWeak();
|
|
}
|
|
return this._refcount;
|
|
}
|
|
deref() {
|
|
const persistent = this.getPersistent();
|
|
return persistent.deref();
|
|
}
|
|
get() {
|
|
const persistent = this.getPersistent();
|
|
if (persistent.isEmpty()) {
|
|
return 0;
|
|
}
|
|
return persistent.slot();
|
|
}
|
|
resetFinalizer() { }
|
|
data() { return 0; }
|
|
refcount() { return this._refcount; }
|
|
ownership() { return this._ownership; }
|
|
callUserFinalizer() { }
|
|
invokeFinalizerFromGC() {
|
|
this.finalize();
|
|
}
|
|
_setWeak() {
|
|
this.setWeakWithData(this, Reference.weakCallback);
|
|
}
|
|
setWeakWithData(data, callback) {
|
|
const persistent = this.getPersistent();
|
|
if (this.canBeWeak) {
|
|
persistent.setWeak(data, callback);
|
|
}
|
|
else {
|
|
persistent.reset();
|
|
}
|
|
}
|
|
clearWeak() {
|
|
const persistent = this.getPersistent();
|
|
persistent.clearWeak();
|
|
}
|
|
finalize() {
|
|
const persistent = this.getPersistent();
|
|
persistent.reset();
|
|
const deleteMe = this._ownership === ReferenceOwnership.kRuntime;
|
|
this.unlink();
|
|
this.callUserFinalizer();
|
|
if (deleteMe) {
|
|
this.dispose();
|
|
}
|
|
}
|
|
dispose() {
|
|
if (this.id === 0)
|
|
return;
|
|
this.unlink();
|
|
this.ctx.isolate.removeRef(this.id);
|
|
this.ctx.refStore.delete(this.id);
|
|
super.dispose();
|
|
this.ctx = undefined;
|
|
this.envObject = undefined;
|
|
this.id = 0;
|
|
}
|
|
}
|
|
class ReferenceWithData extends Reference {
|
|
static create(ctx, envObject, value, initialRefcount, ownership, data) {
|
|
const reference = new ReferenceWithData(ctx, envObject, value, initialRefcount, ownership, data);
|
|
if (envObject) {
|
|
reference.link(envObject.reflist);
|
|
}
|
|
return reference;
|
|
}
|
|
constructor(ctx, envObject, value, initialRefcount, ownership, data) {
|
|
super(ctx, envObject, value, initialRefcount, ownership);
|
|
this._data = data;
|
|
}
|
|
data() {
|
|
return this._data;
|
|
}
|
|
}
|
|
class ReferenceWithFinalizer extends Reference {
|
|
static create(ctx, envObject, value, initialRefcount, ownership, finalize_callback, finalize_data, finalize_hint) {
|
|
if (!envObject) {
|
|
throw new TypeError('envObject is required for ReferenceWithFinalizer');
|
|
}
|
|
const reference = new ReferenceWithFinalizer(ctx, envObject, value, initialRefcount, ownership, finalize_callback, finalize_data, finalize_hint);
|
|
reference.link(envObject.finalizing_reflist);
|
|
return reference;
|
|
}
|
|
constructor(ctx, envObject, value, initialRefcount, ownership, finalize_callback, finalize_data, finalize_hint) {
|
|
super(ctx, envObject, value, initialRefcount, ownership);
|
|
this._finalizer = new Finalizer(envObject, finalize_callback, finalize_data, finalize_hint);
|
|
}
|
|
resetFinalizer() {
|
|
this._finalizer.resetFinalizer();
|
|
}
|
|
data() {
|
|
return this._finalizer.data();
|
|
}
|
|
callUserFinalizer() {
|
|
this._finalizer.callFinalizer();
|
|
}
|
|
invokeFinalizerFromGC() {
|
|
this._finalizer.envObject.invokeFinalizerFromGC(this);
|
|
}
|
|
dispose() {
|
|
if (!this._finalizer)
|
|
return;
|
|
this._finalizer.envObject.dequeueFinalizer(this);
|
|
this._finalizer.dispose();
|
|
super.dispose();
|
|
this._finalizer = undefined;
|
|
}
|
|
}
|
|
|
|
//#endregion src/Reference.ts
|
|
|
|
//#region src/Handle.ts
|
|
class HandleStore extends BaseArrayStore {
|
|
constructor(features) {
|
|
super(HandleStore.MIN_ID);
|
|
this._features = features;
|
|
this._allocator = new CountIdAllocator(HandleStore.MIN_ID);
|
|
this.refValues = new Map();
|
|
this._values[2] = undefined;
|
|
this._values[3] = null;
|
|
this._values[4] = false;
|
|
this._values[5] = true;
|
|
this._values[6] = features.getGlobalThis();
|
|
this._values[7] = '';
|
|
const _erase = (start, end) => {
|
|
for (let i = start; i < end; ++i) {
|
|
this.dealloc(i);
|
|
}
|
|
};
|
|
this._erase = this._features.finalizer
|
|
? this._features.weakSymbol
|
|
? (start, end, weak) => {
|
|
if (!weak) {
|
|
_erase(start, end);
|
|
}
|
|
else {
|
|
for (let i = start; i < end; ++i) {
|
|
const value = this._values[i];
|
|
const type = typeof value;
|
|
if ((type === 'object' && value !== null) || (type === 'function') || (type === 'symbol' && Symbol.keyFor(value) === undefined)) {
|
|
this._values[i] = new WeakRef(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
: (start, end, weak) => {
|
|
if (!weak) {
|
|
_erase(start, end);
|
|
}
|
|
else {
|
|
for (let i = start; i < end; ++i) {
|
|
const value = this._values[i];
|
|
const type = typeof value;
|
|
if ((type === 'object' && value !== null) || (type === 'function')) {
|
|
this._values[i] = new WeakRef(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
: _erase;
|
|
this._deref = this._features.finalizer
|
|
? (id) => {
|
|
id = Number(id);
|
|
if (id < 0 || id > 0x7fffffff) {
|
|
const ref = this.refValues.get(id >>> 0);
|
|
if (ref === undefined)
|
|
return undefined;
|
|
return ref.deref();
|
|
}
|
|
const value = this._values[id];
|
|
if (value instanceof WeakRef && this.isOutOfScope(id)) {
|
|
return value.deref();
|
|
}
|
|
return value;
|
|
}
|
|
: (id) => {
|
|
id = Number(id);
|
|
if (id < 0 || id > 0x7fffffff) {
|
|
const ref = this.refValues.get(id >>> 0);
|
|
if (ref === undefined)
|
|
return undefined;
|
|
return ref.deref();
|
|
}
|
|
const value = this._values[id];
|
|
return value;
|
|
};
|
|
}
|
|
isOutOfScope(id) {
|
|
return id >= this._allocator.next;
|
|
}
|
|
deepDeref(id) {
|
|
return this._deref(id);
|
|
}
|
|
push(value) {
|
|
const next = this._allocator.aquire();
|
|
this._values[next] = value;
|
|
return next;
|
|
}
|
|
erase(start, end, weak) {
|
|
this._allocator.next = start;
|
|
this._erase(start, end, weak);
|
|
}
|
|
swap(a, b) {
|
|
const values = this._values;
|
|
const h = values[a];
|
|
values[a] = values[b];
|
|
values[b] = h;
|
|
}
|
|
}
|
|
HandleStore.MIN_ID = 8;
|
|
|
|
//#endregion src/Handle.ts
|
|
|
|
//#region src/External.ts
|
|
const externalValue = new WeakMap();
|
|
function isExternal(object) {
|
|
return externalValue.has(object);
|
|
}
|
|
const External = (() => {
|
|
function External(value) {
|
|
Object.setPrototypeOf(this, null);
|
|
externalValue.set(this, value);
|
|
}
|
|
External.prototype = null;
|
|
return External;
|
|
})();
|
|
function getExternalValue(external) {
|
|
if (!isExternal(external)) {
|
|
throw new TypeError('not external');
|
|
}
|
|
return externalValue.get(external);
|
|
}
|
|
|
|
//#endregion src/External.ts
|
|
|
|
//#region src/HandleScope.ts
|
|
class HandleScope extends Disposable {
|
|
static create(parentScope, handleStore, start = parentScope?.end ?? 1, end = start) {
|
|
return new HandleScope(parentScope, handleStore, start, end);
|
|
}
|
|
constructor(parentScope, handleStore, start = parentScope?.end ?? 1, end = start) {
|
|
super();
|
|
this._escapeCalled = false;
|
|
this.handleStore = handleStore;
|
|
this.id = 0;
|
|
this.parent = parentScope;
|
|
this.child = null;
|
|
if (parentScope !== null)
|
|
parentScope.child = this;
|
|
this.start = start;
|
|
this.end = end;
|
|
this.callbackInfo = {
|
|
thiz: undefined,
|
|
holder: undefined,
|
|
data: 0,
|
|
args: undefined,
|
|
fn: undefined
|
|
};
|
|
}
|
|
reuse(parentScope) {
|
|
this.start = this.end = parentScope.end;
|
|
this._escapeCalled = false;
|
|
}
|
|
add(value) {
|
|
const h = this.handleStore.push(value);
|
|
this.end = h + 1;
|
|
return h;
|
|
}
|
|
addExternal(data) {
|
|
return this.add(new External(data));
|
|
}
|
|
dispose() {
|
|
const weak = this.callbackInfo.fn === undefined;
|
|
if (!weak) {
|
|
this.callbackInfo.data = 0;
|
|
this.callbackInfo.args = undefined;
|
|
this.callbackInfo.thiz = undefined;
|
|
this.callbackInfo.holder = undefined;
|
|
this.callbackInfo.fn = undefined;
|
|
}
|
|
if (this.start === this.end)
|
|
return;
|
|
this.handleStore.erase(this.start, this.end, weak);
|
|
}
|
|
escape(handle) {
|
|
if (handle < this.start || handle >= this.end)
|
|
return handle;
|
|
if (this._escapeCalled)
|
|
return 0;
|
|
this._escapeCalled = true;
|
|
if (handle < this.start || handle >= this.end) {
|
|
return 0;
|
|
}
|
|
const id = this.start;
|
|
this.handleStore.swap(handle, id);
|
|
this.start++;
|
|
this.parent.end++;
|
|
return id;
|
|
}
|
|
escapeCalled() {
|
|
return this._escapeCalled;
|
|
}
|
|
}
|
|
|
|
//#endregion src/HandleScope.ts
|
|
|
|
//#region src/ScopeStore.ts
|
|
class ScopeStore extends BaseArrayStore {
|
|
constructor() {
|
|
super();
|
|
this._rootScope = new HandleScope(null, null, 1, HandleStore.MIN_ID);
|
|
this.currentScope = this._rootScope;
|
|
}
|
|
openScope(handleStore) {
|
|
const currentScope = this.currentScope;
|
|
let scope = currentScope.child;
|
|
if (scope) {
|
|
scope.reuse(currentScope);
|
|
}
|
|
else {
|
|
scope = new HandleScope(currentScope, handleStore);
|
|
const id = currentScope.id + 1;
|
|
scope.id = id;
|
|
this._values[id] = scope;
|
|
}
|
|
this.currentScope = scope;
|
|
return scope;
|
|
}
|
|
closeScope() {
|
|
const scope = this.currentScope;
|
|
this.currentScope = scope.parent;
|
|
scope.dispose();
|
|
}
|
|
isEmpty() {
|
|
return this.currentScope === this._rootScope;
|
|
}
|
|
}
|
|
|
|
//#endregion src/ScopeStore.ts
|
|
|
|
//#region src/TryCatch.ts
|
|
class TryCatch {
|
|
constructor(id) {
|
|
this.id = 0;
|
|
this._exception = undefined;
|
|
this._caught = false;
|
|
this._next = null;
|
|
this.id = id;
|
|
this._next = TryCatch.top;
|
|
TryCatch.top = this;
|
|
TryCatch._map.set(id, this);
|
|
}
|
|
static deref(id) {
|
|
return this._map.get(id);
|
|
}
|
|
static pop() {
|
|
const top = TryCatch.top;
|
|
if (top) {
|
|
TryCatch._map.delete(top.id);
|
|
TryCatch.top = top._next;
|
|
top._next = null;
|
|
top.id = 0;
|
|
top._exception = undefined;
|
|
top._caught = false;
|
|
}
|
|
}
|
|
isEmpty() {
|
|
return !this._caught;
|
|
}
|
|
hasCaught() {
|
|
return this._caught;
|
|
}
|
|
exception() {
|
|
return this._exception;
|
|
}
|
|
rethrow(ctx) {
|
|
if (this._caught) {
|
|
const e = this.extractException();
|
|
ctx.setLastException(e);
|
|
return e;
|
|
}
|
|
return undefined;
|
|
}
|
|
setError(err) {
|
|
this._caught = true;
|
|
this._exception = err;
|
|
}
|
|
reset() {
|
|
this._caught = false;
|
|
this._exception = undefined;
|
|
}
|
|
extractException() {
|
|
const e = this._exception;
|
|
this.reset();
|
|
return e;
|
|
}
|
|
}
|
|
TryCatch.top = null;
|
|
TryCatch._map = new Map();
|
|
|
|
//#endregion src/TryCatch.ts
|
|
|
|
//#region src/Template.ts
|
|
var PropertyAttribute;
|
|
(function (PropertyAttribute) {
|
|
PropertyAttribute[PropertyAttribute["None"] = 0] = "None";
|
|
PropertyAttribute[PropertyAttribute["ReadOnly"] = 1] = "ReadOnly";
|
|
PropertyAttribute[PropertyAttribute["DontEnum"] = 2] = "DontEnum";
|
|
PropertyAttribute[PropertyAttribute["DontDelete"] = 4] = "DontDelete";
|
|
})(PropertyAttribute || (PropertyAttribute = {}));
|
|
class Template {
|
|
constructor(ctx) {
|
|
this._properties = new Map();
|
|
this.ctx = ctx;
|
|
}
|
|
set(name, value, attr) {
|
|
this._properties.set(name, [value, attr]);
|
|
}
|
|
_addPropertiesToInstance(instance) {
|
|
this._properties.forEach((property, name) => {
|
|
const [v, attr] = property;
|
|
if (v instanceof Template) {
|
|
if ('newInstance' in v) {
|
|
Object.defineProperty(instance, name, {
|
|
value: v.newInstance(6),
|
|
writable: !(attr & 1),
|
|
enumerable: !(attr & 2),
|
|
configurable: !(attr & 4)
|
|
});
|
|
}
|
|
else if ('getFunction' in v) {
|
|
Object.defineProperty(instance, name, {
|
|
value: v.getFunction(),
|
|
writable: !(attr & 1),
|
|
enumerable: !(attr & 2),
|
|
configurable: !(attr & 4)
|
|
});
|
|
}
|
|
}
|
|
else {
|
|
Object.defineProperty(instance, name, {
|
|
value: v,
|
|
writable: !(attr & 1),
|
|
enumerable: !(attr & 2),
|
|
configurable: !(attr & 4)
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
//#endregion src/Template.ts
|
|
|
|
//#region src/ObjectTemplate.ts
|
|
function findHolder(obj, _target) {
|
|
return obj;
|
|
}
|
|
const internalField = new WeakMap();
|
|
function getInternalFieldCount(instance) {
|
|
return internalField.get(instance)?.length ?? 0;
|
|
}
|
|
function getInternalField(instance, index) {
|
|
return internalField.get(instance)?.[index];
|
|
}
|
|
function setInternalField(instance, index, value) {
|
|
let fields = internalField.get(instance);
|
|
if (fields) {
|
|
fields[index] = value;
|
|
}
|
|
else {
|
|
fields = [];
|
|
fields[index] = value;
|
|
internalField.set(instance, fields);
|
|
}
|
|
}
|
|
class ObjectTemplate extends Template {
|
|
constructor(ctx, Ctor) {
|
|
super(ctx);
|
|
this.internalFieldCount = 0;
|
|
this._accessors = new Map();
|
|
this.Ctor = Ctor ?? Object;
|
|
}
|
|
setAccessor(name, getterWrap, setterWrap, getter, setter, data, attribute, getterSideEffectType, setterSideEffectType) {
|
|
this._accessors.set(name, {
|
|
name,
|
|
getterWrap,
|
|
setterWrap,
|
|
getter,
|
|
setter,
|
|
data,
|
|
attribute,
|
|
getterSideEffectType,
|
|
setterSideEffectType
|
|
});
|
|
}
|
|
setInternalFieldCount(value) {
|
|
this.internalFieldCount = value;
|
|
}
|
|
applyToInstance(instance) {
|
|
internalField.set(instance, Array(this.internalFieldCount));
|
|
this._addPropertiesToInstance(instance);
|
|
const createAccessorWrapper = (type, data, cb) => {
|
|
const { ctx } = this;
|
|
function _(value) {
|
|
const scope = ctx.openScope();
|
|
const callbackInfo = scope.callbackInfo;
|
|
let returnValue;
|
|
try {
|
|
callbackInfo.data = data;
|
|
callbackInfo.args = type === 'getter' ? [] : [value];
|
|
callbackInfo.thiz = this;
|
|
callbackInfo.holder = findHolder(this, _) || this;
|
|
callbackInfo.fn = _;
|
|
const ret = cb(value);
|
|
returnValue = ret ? ctx.jsValueFromNapiValue(ret) : undefined;
|
|
}
|
|
catch (err) {
|
|
ctx.throwException(err);
|
|
}
|
|
ctx.closeScope(scope);
|
|
if (ctx.hasPendingException()) {
|
|
if (TryCatch.top) {
|
|
TryCatch.top.setError(ctx.getAndClearLastException());
|
|
}
|
|
else {
|
|
throw ctx.getAndClearLastException();
|
|
}
|
|
}
|
|
return returnValue;
|
|
}
|
|
return _;
|
|
};
|
|
this._accessors.forEach((config, name) => {
|
|
const { ctx } = this;
|
|
const { getterWrap, setterWrap, getter, setter, data, attribute } = config;
|
|
Object.defineProperty(instance, name, {
|
|
get: getter
|
|
? createAccessorWrapper('getter', data, () => {
|
|
return getterWrap(ctx.napiValueFromJsValue(name), ctx.getCurrentScope().id, getter);
|
|
}).bind(instance)
|
|
: undefined,
|
|
set: setter
|
|
? createAccessorWrapper('setter', data, (value) => {
|
|
return setterWrap(ctx.napiValueFromJsValue(name), ctx.napiValueFromJsValue(value), ctx.getCurrentScope().id, setter);
|
|
}).bind(instance)
|
|
: undefined,
|
|
enumerable: !(attribute & 2),
|
|
configurable: !(attribute & 4)
|
|
});
|
|
});
|
|
}
|
|
newInstance(_context) {
|
|
const { ctx, Ctor } = this;
|
|
let instance;
|
|
try {
|
|
instance = new Ctor();
|
|
}
|
|
catch (err) {
|
|
ctx.throwException(err);
|
|
}
|
|
this.applyToInstance(instance);
|
|
return instance;
|
|
}
|
|
}
|
|
|
|
//#endregion src/ObjectTemplate.ts
|
|
|
|
//#region src/FunctionTemplate.ts
|
|
class Signature {
|
|
constructor(receiver) {
|
|
this.receiver = receiver;
|
|
}
|
|
}
|
|
class FunctionTemplate extends Template {
|
|
constructor(ctx, callback, v8FunctionCallback, data, signature) {
|
|
super(ctx);
|
|
this.ctx = ctx;
|
|
this.callback = callback;
|
|
this.v8FunctionCallback = v8FunctionCallback;
|
|
this.data = data;
|
|
this.className = undefined;
|
|
this.signature = signature;
|
|
this._cached = [undefined];
|
|
}
|
|
setClassName(name) {
|
|
this.className = name;
|
|
}
|
|
instanceTemplate() {
|
|
if (!this._instanceTemplate) {
|
|
this._instanceTemplate = new ObjectTemplate(this.ctx);
|
|
}
|
|
return this._instanceTemplate;
|
|
}
|
|
prototypeTemplate() {
|
|
if (!this._prototypeTemplate) {
|
|
this._prototypeTemplate = new ObjectTemplate(this.ctx);
|
|
}
|
|
return this._prototypeTemplate;
|
|
}
|
|
getFunction() {
|
|
if (this._cached[0]) {
|
|
return this._cached[0];
|
|
}
|
|
const { ctx, callback, v8FunctionCallback, data, signature, _instanceTemplate } = this;
|
|
function _(...args) {
|
|
if (signature && signature.receiver) {
|
|
const f = signature.receiver.getFunction();
|
|
if (!(this instanceof f)) {
|
|
throw new TypeError('Illegal invocation');
|
|
}
|
|
}
|
|
const scope = ctx.openScope();
|
|
const callbackInfo = scope.callbackInfo;
|
|
let returnValue;
|
|
try {
|
|
callbackInfo.data = data;
|
|
callbackInfo.args = args;
|
|
callbackInfo.thiz = this;
|
|
callbackInfo.holder = findHolder(this, _) || this;
|
|
callbackInfo.fn = _;
|
|
if (_instanceTemplate && this instanceof _) {
|
|
_instanceTemplate.applyToInstance(this);
|
|
}
|
|
const ret = callback(ctx.getCurrentScope().id, v8FunctionCallback);
|
|
returnValue = ret ? ctx.jsValueFromNapiValue(ret) : undefined;
|
|
}
|
|
catch (err) {
|
|
if (err !== 'unwind') {
|
|
ctx.throwException(err);
|
|
}
|
|
}
|
|
finally {
|
|
ctx.closeScope(scope);
|
|
}
|
|
if (ctx.hasPendingException()) {
|
|
if (TryCatch.top) {
|
|
TryCatch.top.setError(ctx.getAndClearLastException());
|
|
}
|
|
else {
|
|
throw ctx.getAndClearLastException();
|
|
}
|
|
}
|
|
return returnValue;
|
|
}
|
|
if (typeof this.className === 'string') {
|
|
this.ctx.features.setFunctionName?.(_, this.className);
|
|
}
|
|
if (this._prototypeTemplate) {
|
|
this._prototypeTemplate.applyToInstance(_.prototype);
|
|
}
|
|
this._addPropertiesToInstance(_);
|
|
this._cached[0] = _;
|
|
return _;
|
|
}
|
|
}
|
|
|
|
//#endregion src/FunctionTemplate.ts
|
|
|
|
//#region src/Private.ts
|
|
const globalPrivateRegistry = new Map();
|
|
const map = new WeakMap();
|
|
class Private {
|
|
constructor(name) {
|
|
this._name = name;
|
|
}
|
|
name() {
|
|
return this._name;
|
|
}
|
|
static forApi(name) {
|
|
let privateInstance = globalPrivateRegistry.get(name);
|
|
if (!privateInstance) {
|
|
privateInstance = new Private(name);
|
|
globalPrivateRegistry.set(name, privateInstance);
|
|
}
|
|
return privateInstance;
|
|
}
|
|
}
|
|
function setPrivate(obj, key, value) {
|
|
if (!(key instanceof Private)) {
|
|
throw new TypeError('Expected key to be an instance of Private');
|
|
}
|
|
let m = map.get(key);
|
|
if (!m) {
|
|
m = new WeakMap();
|
|
map.set(key, m);
|
|
}
|
|
m.set(obj, value);
|
|
}
|
|
function getPrivate(obj, key) {
|
|
if (!(key instanceof Private)) {
|
|
throw new TypeError('Expected key to be an instance of Private');
|
|
}
|
|
const m = map.get(key);
|
|
if (!m) {
|
|
return undefined;
|
|
}
|
|
return m.get(obj);
|
|
}
|
|
function hasPrivate(obj, key) {
|
|
if (!(key instanceof Private)) {
|
|
throw new TypeError('Expected key to be an instance of Private');
|
|
}
|
|
const m = map.get(key);
|
|
if (!m) {
|
|
return false;
|
|
}
|
|
return m.has(obj);
|
|
}
|
|
function deletePrivate(obj, key) {
|
|
if (!(key instanceof Private)) {
|
|
throw new TypeError('Expected key to be an instance of Private');
|
|
}
|
|
const m = map.get(key);
|
|
if (!m || !m.has(obj)) {
|
|
return true;
|
|
}
|
|
return m.delete(obj);
|
|
}
|
|
|
|
//#endregion src/Private.ts
|
|
|
|
//#region src/ExternalMemory.ts
|
|
const kMaxReasonableBytes = BigInt(1) << BigInt(60);
|
|
const kMinReasonableBytes = -kMaxReasonableBytes;
|
|
class ExternalMemory {
|
|
constructor(onChange) {
|
|
this.total = BigInt(0);
|
|
this.onChange = onChange ?? null;
|
|
}
|
|
adjust(changeInBytes) {
|
|
changeInBytes = BigInt(changeInBytes);
|
|
if (!(kMinReasonableBytes <= changeInBytes && changeInBytes < kMaxReasonableBytes)) {
|
|
throw new RangeError(`changeInBytes ${changeInBytes} is out of reasonable range`);
|
|
}
|
|
const old = this.total;
|
|
this.total += changeInBytes;
|
|
const amount = this.total;
|
|
const onChange = this.onChange;
|
|
if (changeInBytes) {
|
|
onChange?.(amount, old, changeInBytes);
|
|
}
|
|
return amount;
|
|
}
|
|
}
|
|
|
|
//#endregion src/ExternalMemory.ts
|
|
|
|
//#region src/Isolate.ts
|
|
class Isolate {
|
|
constructor(options) {
|
|
this.features = detectFeatures(options?.features);
|
|
this._globalThis = this.features.getGlobalThis();
|
|
this._scopeStore = new ScopeStore();
|
|
this._handleStore = new HandleStore(this.features);
|
|
this.globalHandleStore = new PersistentStore();
|
|
this._lastException = new Persistent(this);
|
|
this._externalMemory = new ExternalMemory(options?.onExternalMemoryChange);
|
|
}
|
|
napiValueFromJsValue(value) {
|
|
switch (value) {
|
|
case undefined: return 2;
|
|
case null: return 3;
|
|
case false: return 4;
|
|
case true: return 5;
|
|
case '': return 7;
|
|
case this._globalThis: return 6;
|
|
default: return this._scopeStore.currentScope.add(value);
|
|
}
|
|
}
|
|
jsValueFromNapiValue(napiValue) {
|
|
return this._handleStore.deepDeref(napiValue);
|
|
}
|
|
deleteRefSlotValue(id) {
|
|
this._handleStore.refValues.delete(Number(id));
|
|
}
|
|
getRefSlotValue(id) {
|
|
return this._handleStore.refValues.get(Number(id));
|
|
}
|
|
setRefSlotValue(id, ref) {
|
|
this._handleStore.refValues.set(Number(id), ref);
|
|
}
|
|
createReference(...args) {
|
|
return new Persistent(this, ...args);
|
|
}
|
|
insertRef(persistent) {
|
|
this.globalHandleStore.insert(persistent);
|
|
}
|
|
getRef(ref) {
|
|
return this.globalHandleStore.deref(ref);
|
|
}
|
|
removeRef(ref, force = false) {
|
|
if (force || this._scopeStore.isEmpty()) {
|
|
const persistent = this.globalHandleStore.deref(ref);
|
|
persistent?.dispose();
|
|
}
|
|
else {
|
|
this.globalHandleStore.markUnused(ref);
|
|
}
|
|
}
|
|
getTryCatch(address) {
|
|
return TryCatch.deref(address);
|
|
}
|
|
pushTryCatch(address) {
|
|
const tryCatch = new TryCatch(address);
|
|
return tryCatch;
|
|
}
|
|
popTryCatch(address) {
|
|
if (address !== TryCatch.top?.id) {
|
|
throw new Error('TryCatch mismatch');
|
|
}
|
|
return TryCatch.pop();
|
|
}
|
|
setLastException(err) {
|
|
this._lastException.resetTo(err);
|
|
}
|
|
throwException(err) {
|
|
if (TryCatch.top) {
|
|
TryCatch.top.setError(err);
|
|
}
|
|
else {
|
|
this.setLastException(err);
|
|
}
|
|
return err;
|
|
}
|
|
hasPendingException() {
|
|
return !this._lastException.isEmpty();
|
|
}
|
|
getAndClearLastException() {
|
|
const err = this._lastException.deref();
|
|
this._lastException.reset();
|
|
return err;
|
|
}
|
|
adjustAmountOfExternalAllocatedMemory(changeInBytes) {
|
|
return this._externalMemory.adjust(changeInBytes);
|
|
}
|
|
createSignature(template) {
|
|
return new Signature(template);
|
|
}
|
|
createObjectTemplate(constructor) {
|
|
return new ObjectTemplate(this, constructor);
|
|
}
|
|
setInternalField(obj, index, value) {
|
|
setInternalField(obj, index, value);
|
|
}
|
|
getInternalField(obj, index) {
|
|
return getInternalField(obj, index);
|
|
}
|
|
getInternalFieldCount(obj) {
|
|
return getInternalFieldCount(obj);
|
|
}
|
|
createFunctionTemplate(callback, v8FunctionCallback, data, signature) {
|
|
const functionTemplate = new FunctionTemplate(this, callback, v8FunctionCallback, data, signature);
|
|
return functionTemplate;
|
|
}
|
|
isScopeEmpty() {
|
|
return this._scopeStore.isEmpty();
|
|
}
|
|
getCurrentScope() {
|
|
return this._scopeStore.currentScope;
|
|
}
|
|
openScope() {
|
|
return this._scopeStore.openScope(this._handleStore);
|
|
}
|
|
closeScope(_scope) {
|
|
this._scopeStore.closeScope();
|
|
if (this.isScopeEmpty()) {
|
|
this.globalHandleStore.recycle();
|
|
}
|
|
}
|
|
getHandleScope(scope) {
|
|
return this._scopeStore.deref(scope);
|
|
}
|
|
getCallbackInfo(info) {
|
|
return this._scopeStore.deref(info).callbackInfo;
|
|
}
|
|
createExternal(data) {
|
|
return new External(data);
|
|
}
|
|
getExternalValue(external) {
|
|
return getExternalValue(external);
|
|
}
|
|
isExternal(value) {
|
|
return isExternal(value);
|
|
}
|
|
createResolver() {
|
|
return this.features.withResolvers.call(Promise);
|
|
}
|
|
createPrivate(name) {
|
|
return new Private(name);
|
|
}
|
|
getOrCreateGlobalPrivate(name) {
|
|
return Private.forApi(name);
|
|
}
|
|
setPrivate(obj, key, value) {
|
|
return setPrivate(obj, key, value);
|
|
}
|
|
getPrivate(obj, key) {
|
|
return getPrivate(obj, key);
|
|
}
|
|
hasPrivate(obj, key) {
|
|
return hasPrivate(obj, key);
|
|
}
|
|
deletePrivate(obj, key) {
|
|
return deletePrivate(obj, key);
|
|
}
|
|
}
|
|
|
|
//#endregion src/Isolate.ts
|
|
|
|
//#region src/Context.ts
|
|
const callbackWrapper = (envObject, callback) => {
|
|
const napiValue = envObject.callIntoModule(callback);
|
|
return (!napiValue) ? undefined : envObject.ctx.jsValueFromNapiValue(napiValue);
|
|
};
|
|
const withScope = (envObject, thiz, args, data, getFunction, wrapper, callback) => {
|
|
const scope = envObject.ctx.openScope(envObject);
|
|
const callbackInfo = scope.callbackInfo;
|
|
callbackInfo.data = data;
|
|
callbackInfo.args = args;
|
|
callbackInfo.thiz = thiz;
|
|
callbackInfo.fn = getFunction;
|
|
try {
|
|
return wrapper(envObject, callback);
|
|
}
|
|
catch (e) {
|
|
if (e !== 'unwind') {
|
|
throw e;
|
|
}
|
|
}
|
|
finally {
|
|
envObject.ctx.closeScope(envObject, scope);
|
|
}
|
|
};
|
|
class CleanupHookCallback {
|
|
constructor(envObject, fn, arg, order) {
|
|
this.envObject = envObject;
|
|
this.fn = fn;
|
|
this.arg = arg;
|
|
this.order = order;
|
|
}
|
|
}
|
|
class CleanupQueue {
|
|
constructor() {
|
|
this._cleanupHooks = [];
|
|
this._cleanupHookCounter = 0;
|
|
}
|
|
empty() {
|
|
return this._cleanupHooks.length === 0;
|
|
}
|
|
add(envObject, fn, arg) {
|
|
if (this._cleanupHooks.filter((hook) => (hook.envObject === envObject && hook.fn === fn && hook.arg === arg)).length > 0) {
|
|
throw new Error('Can not add same fn and arg twice');
|
|
}
|
|
this._cleanupHooks.push(new CleanupHookCallback(envObject, fn, arg, this._cleanupHookCounter++));
|
|
}
|
|
remove(envObject, fn, arg) {
|
|
for (let i = 0; i < this._cleanupHooks.length; ++i) {
|
|
const hook = this._cleanupHooks[i];
|
|
if (hook.envObject === envObject && hook.fn === fn && hook.arg === arg) {
|
|
this._cleanupHooks.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
drain() {
|
|
const hooks = this._cleanupHooks.slice();
|
|
hooks.sort((a, b) => (b.order - a.order));
|
|
for (let i = 0; i < hooks.length; ++i) {
|
|
const cb = hooks[i];
|
|
if (typeof cb.fn === 'number') {
|
|
cb.envObject.bridge.makeDynCall_vp(cb.fn)(cb.arg);
|
|
}
|
|
else {
|
|
cb.fn(cb.arg);
|
|
}
|
|
this._cleanupHooks.splice(this._cleanupHooks.indexOf(cb), 1);
|
|
}
|
|
}
|
|
dispose() {
|
|
this._cleanupHooks.length = 0;
|
|
this._cleanupHookCounter = 0;
|
|
}
|
|
}
|
|
class NodejsWaitingRequestCounter {
|
|
constructor(MessageChannel) {
|
|
this.refHandle = new MessageChannel().port1;
|
|
this.count = 0;
|
|
}
|
|
increase() {
|
|
if (this.count === 0) {
|
|
if (this.refHandle.ref) {
|
|
this.refHandle.ref();
|
|
}
|
|
}
|
|
this.count++;
|
|
}
|
|
decrease() {
|
|
if (this.count === 0)
|
|
return;
|
|
if (this.count === 1) {
|
|
if (this.refHandle.unref) {
|
|
this.refHandle.unref();
|
|
}
|
|
}
|
|
this.count--;
|
|
}
|
|
}
|
|
class Context {
|
|
constructor(options) {
|
|
this._isStopping = false;
|
|
this._canCallIntoJs = true;
|
|
this._suppressDestroy = false;
|
|
this.envStore = new ArrayStore();
|
|
this.refStore = new Map();
|
|
this.isolate = new Isolate(options);
|
|
this.features = this.isolate.features;
|
|
this.cleanupQueue = new CleanupQueue();
|
|
if (typeof process === 'object' && process !== null && typeof process.once === 'function' && this.features.MessageChannel) {
|
|
this.refCounter = new NodejsWaitingRequestCounter(this.features.MessageChannel);
|
|
process.once('beforeExit', () => {
|
|
if (!this._suppressDestroy) {
|
|
this.destroy();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
getIsolate() {
|
|
return this.isolate;
|
|
}
|
|
suppressDestroy() {
|
|
this._suppressDestroy = true;
|
|
}
|
|
getRuntimeVersions() {
|
|
return {
|
|
version,
|
|
NODE_API_SUPPORTED_VERSION_MAX,
|
|
NAPI_VERSION_EXPERIMENTAL,
|
|
NODE_API_DEFAULT_MODULE_API_VERSION,
|
|
NODE_MODULE_VERSION
|
|
};
|
|
}
|
|
createNotSupportWeakRefError(api, message) {
|
|
return new NotSupportWeakRefError(api, message);
|
|
}
|
|
createNotSupportBufferError(api, message) {
|
|
return new NotSupportBufferError(api, message);
|
|
}
|
|
createReference(envObject, handle_id, initialRefcount, ownership) {
|
|
return Reference.create(this, envObject, handle_id, initialRefcount, ownership);
|
|
}
|
|
createReferenceWithData(envObject, handle_id, initialRefcount, ownership, data) {
|
|
return ReferenceWithData.create(this, envObject, handle_id, initialRefcount, ownership, data);
|
|
}
|
|
createReferenceWithFinalizer(envObject, handle_id, initialRefcount, ownership, finalize_callback = 0, finalize_data = 0, finalize_hint = 0) {
|
|
return ReferenceWithFinalizer.create(this, envObject, handle_id, initialRefcount, ownership, finalize_callback, finalize_data, finalize_hint);
|
|
}
|
|
createResolver() {
|
|
return this.isolate.createResolver();
|
|
}
|
|
adjustAmountOfExternalAllocatedMemory(changeInBytes) {
|
|
return this.isolate.adjustAmountOfExternalAllocatedMemory(changeInBytes);
|
|
}
|
|
createEnv(filename, moduleApiVersion, bridge, nodeBinding) {
|
|
moduleApiVersion = typeof moduleApiVersion !== 'number' ? NODE_API_DEFAULT_MODULE_API_VERSION : moduleApiVersion;
|
|
if (moduleApiVersion < NODE_API_DEFAULT_MODULE_API_VERSION) {
|
|
moduleApiVersion = NODE_API_DEFAULT_MODULE_API_VERSION;
|
|
}
|
|
else if (moduleApiVersion > NODE_API_SUPPORTED_VERSION_MAX && moduleApiVersion !== NAPI_VERSION_EXPERIMENTAL) {
|
|
const errorMessage = `${filename} requires Node-API version ${moduleApiVersion}, but this version of Node.js only supports version ${NODE_API_SUPPORTED_VERSION_MAX} add-ons.`;
|
|
throw new Error(errorMessage);
|
|
}
|
|
const env = new NodeEnv(this, this.envStore, { ...bridge });
|
|
env.filename = filename;
|
|
env.moduleApiVersion = moduleApiVersion;
|
|
env.nodeBinding = nodeBinding;
|
|
this.addCleanupHook(env, () => { env.unref(); }, 0);
|
|
return env;
|
|
}
|
|
createFunction(envObject, napiCallback, data, name, dynamicExecution) {
|
|
if (envObject.ctx !== this) {
|
|
throw new Error(`The napi_env (${envObject.bridge.address}) is not created by this context`);
|
|
}
|
|
const callback = (envObject) => {
|
|
return napiCallback(envObject.bridge.address, envObject.ctx.getCurrentScope().id);
|
|
};
|
|
let _;
|
|
const staticFunctionWrapper = (withScope, envObject, data, callbackWrapper, callback) => {
|
|
return function (...args) {
|
|
return withScope(envObject, this, args, data, _, callbackWrapper, callback);
|
|
};
|
|
};
|
|
let functionWrapper;
|
|
if (name && dynamicExecution && this.features.makeDynamicFunction) {
|
|
try {
|
|
functionWrapper = this.features.makeDynamicFunction('withScope', 'envObject', 'data', 'callbackWrapper', 'callback', 'return function ' + name + '(...args){' +
|
|
'return withScope(envObject,this,args,data,' + name + ',callbackWrapper,callback)' +
|
|
'};');
|
|
}
|
|
catch (_) {
|
|
functionWrapper = staticFunctionWrapper;
|
|
}
|
|
}
|
|
else {
|
|
functionWrapper = staticFunctionWrapper;
|
|
}
|
|
_ = functionWrapper(withScope, envObject, data, callbackWrapper, callback);
|
|
if (functionWrapper === staticFunctionWrapper && this.features.setFunctionName && name) {
|
|
this.features.setFunctionName(_, name);
|
|
}
|
|
return _;
|
|
}
|
|
createTrackedFinalizer(envObject, finalize_callback, finalize_data, finalize_hint) {
|
|
return TrackedFinalizer.create(envObject, finalize_callback, finalize_data, finalize_hint);
|
|
}
|
|
getCurrentScope() {
|
|
return this.isolate.getCurrentScope();
|
|
}
|
|
openScope(envObject) {
|
|
const scope = this.isolate.openScope();
|
|
envObject.openHandleScopes++;
|
|
return scope;
|
|
}
|
|
closeScope(envObject, scope) {
|
|
this.isolate.closeScope(scope);
|
|
envObject.openHandleScopes--;
|
|
}
|
|
getEnv(env) {
|
|
return this.envStore.deref(env);
|
|
}
|
|
getRef(ref) {
|
|
return this.refStore.get(Number(ref));
|
|
}
|
|
getHandleScope(scope) {
|
|
return this.isolate.getHandleScope(scope);
|
|
}
|
|
getCallbackInfo(info) {
|
|
return this.isolate.getCallbackInfo(info);
|
|
}
|
|
napiValueFromJsValue(value) {
|
|
return this.isolate.napiValueFromJsValue(value);
|
|
}
|
|
jsValueFromNapiValue(napiValue) {
|
|
return this.isolate.jsValueFromNapiValue(napiValue);
|
|
}
|
|
createExternal(data) {
|
|
return this.isolate.createExternal(data);
|
|
}
|
|
getExternalValue(external) {
|
|
return this.isolate.getExternalValue(external);
|
|
}
|
|
isExternal(value) {
|
|
return this.isolate.isExternal(value);
|
|
}
|
|
addCleanupHook(envObject, fn, arg) {
|
|
this.cleanupQueue.add(envObject, fn, arg);
|
|
}
|
|
removeCleanupHook(envObject, fn, arg) {
|
|
this.cleanupQueue.remove(envObject, fn, arg);
|
|
}
|
|
runCleanup() {
|
|
while (!this.cleanupQueue.empty()) {
|
|
this.cleanupQueue.drain();
|
|
}
|
|
}
|
|
increaseWaitingRequestCounter() {
|
|
this.refCounter?.increase();
|
|
}
|
|
decreaseWaitingRequestCounter() {
|
|
this.refCounter?.decrease();
|
|
}
|
|
setCanCallIntoJs(value) {
|
|
this._canCallIntoJs = value;
|
|
}
|
|
setStopping(value) {
|
|
this._isStopping = value;
|
|
}
|
|
canCallIntoJs() {
|
|
return this._canCallIntoJs && !this._isStopping;
|
|
}
|
|
getDylinkMetadata(binary) {
|
|
return getDylinkMetadata(binary);
|
|
}
|
|
destroy() {
|
|
this.setStopping(true);
|
|
this.setCanCallIntoJs(false);
|
|
this.runCleanup();
|
|
}
|
|
}
|
|
let defaultContext;
|
|
function createContext(options) {
|
|
return new Context(options);
|
|
}
|
|
function getDefaultContext() {
|
|
if (!defaultContext) {
|
|
defaultContext = createContext();
|
|
}
|
|
return defaultContext;
|
|
}
|
|
|
|
//#endregion src/Context.ts
|
|
|
|
export { ArrayStore, BaseArrayStore, Context, CountIdAllocator, CountIdReuseAllocator, Disposable, EmnapiError, Env, External, Finalizer, FunctionTemplate, HandleScope, HandleStore, Isolate, NAPI_VERSION_EXPERIMENTAL, NODE_API_DEFAULT_MODULE_API_VERSION, NODE_API_SUPPORTED_VERSION_MAX, NODE_API_SUPPORTED_VERSION_MIN, NodeEnv, NotSupportBufferError, NotSupportWeakRefError, ObjectTemplate, Persistent, PersistentStore, Private, RefTracker, Reference, ReferenceOwnership, ReferenceWithData, ReferenceWithFinalizer, ScopeStore, Signature, StrongRef, Template, TrackedFinalizer, TryCatch, createContext, deletePrivate, getDefaultContext, getDylinkMetadata, getExternalValue, getInternalField, getPrivate, hasPrivate, isExternal, isReferenceType, setInternalField, setPrivate, version };
|
|
//# sourceMappingURL=emnapi.js.map
|