diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..18ed79c2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "api-extractor.json": "jsonc" + } +} diff --git a/lib/runtime/Deferred.ts b/lib/runtime/Deferred.ts index 95d7170b..fa8bc338 100644 --- a/lib/runtime/Deferred.ts +++ b/lib/runtime/Deferred.ts @@ -1,47 +1,47 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export interface IDeferrdValue { - resolve: (value: T) => void - reject: (reason?: any) => void +import { IStoreValue, Store } from './Store' +import { envStore } from './util' + +export interface IDeferrdValue { + resolve: (value: T) => void + reject: (reason?: any) => void +} + +export class Deferred implements IStoreValue { + public static create (env: napi_env, value: IDeferrdValue): Deferred { + const deferred = new Deferred(env, value) + envStore.get(env)!.deferredStore.add(deferred) + return deferred + } + + public id: number + public env: napi_env + public value: IDeferrdValue + + public constructor (env: napi_env, value: IDeferrdValue) { + this.id = 0 + this.env = env + this.value = value } - export class Deferred implements IStoreValue { - public static create (env: napi_env, value: IDeferrdValue): Deferred { - const deferred = new Deferred(env, value) - envStore.get(env)!.deferredStore.add(deferred) - return deferred - } - - public id: number - public env: napi_env - public value: IDeferrdValue - - public constructor (env: napi_env, value: IDeferrdValue) { - this.id = 0 - this.env = env - this.value = value - } - - public resolve (value: T): void { - this.value.resolve(value) - this.dispose() - } - - public reject (reason?: any): void { - this.value.reject(reason) - this.dispose() - } - - public dispose (): void { - envStore.get(this.env)!.deferredStore.remove(this.id) - this.id = 0 - this.value = null! - } + public resolve (value: T): void { + this.value.resolve(value) + this.dispose() } - export class DeferredStore extends Store { - public constructor () { - super() - } + public reject (reason?: any): void { + this.value.reject(reason) + this.dispose() + } + + public dispose (): void { + envStore.get(this.env)!.deferredStore.remove(this.id) + this.id = 0 + this.value = null! + } +} + +export class DeferredStore extends Store { + public constructor () { + super() } } diff --git a/lib/runtime/Handle.ts b/lib/runtime/Handle.ts index c71d4b5e..ac062733 100644 --- a/lib/runtime/Handle.ts +++ b/lib/runtime/Handle.ts @@ -1,283 +1,285 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export class HandleStore extends Store> { - public static ID_UNDEFINED: -2147483648 = -2147483648 - public static ID_NULL: -2147483647 = -2147483647 - public static ID_FALSE: -2147483646 = -2147483646 - public static ID_TRUE: -2147483645 = -2147483645 - public static ID_GLOBAL: -2147483644 = -2147483644 - - public static get getMinId (): number { - return -2147483643 - } +import { IHandleScope } from './HandleScope' +import { Reference } from './Reference' +import { Store, IStoreValue } from './Store' +import { _global, isReferenceType, envStore } from './util' + +export class HandleStore extends Store> { + public static ID_UNDEFINED: -2147483648 = -2147483648 + public static ID_NULL: -2147483647 = -2147483647 + public static ID_FALSE: -2147483646 = -2147483646 + public static ID_TRUE: -2147483645 = -2147483645 + public static ID_GLOBAL: -2147483644 = -2147483644 + + public static get getMinId (): number { + return -2147483643 + } - public static globalConstants = { - [HandleStore.ID_UNDEFINED]: undefined, - [HandleStore.ID_NULL]: null, - [HandleStore.ID_FALSE]: false, - [HandleStore.ID_TRUE]: true, - [HandleStore.ID_GLOBAL]: _global - } + public static globalConstants = { + [HandleStore.ID_UNDEFINED]: undefined, + [HandleStore.ID_NULL]: null, + [HandleStore.ID_FALSE]: false, + [HandleStore.ID_TRUE]: true, + [HandleStore.ID_GLOBAL]: _global + } - // js object -> Handle - private _objWeakMap: WeakMap> - // js value in store -> Handle id - private _map: Map + // js object -> Handle + private _objWeakMap: WeakMap> + // js value in store -> Handle id + private _map: Map - public constructor () { - super(HandleStore.getMinId) - this._objWeakMap = new WeakMap() - this._map = new Map() - } + public constructor () { + super(HandleStore.getMinId) + this._objWeakMap = new WeakMap() + this._map = new Map() + } - public addGlobalConstants (env: napi_env): void { - Object.keys(HandleStore.globalConstants).forEach(k => { - const id = Number(k) as keyof typeof HandleStore.globalConstants - const value = HandleStore.globalConstants[id] - this.set(id, new Handle(env, id, value)) - this._map.set(value, [id]) - Reference.create(env, id, 1, false) - }) - } + public addGlobalConstants (env: napi_env): void { + Object.keys(HandleStore.globalConstants).forEach(k => { + const id = Number(k) as keyof typeof HandleStore.globalConstants + const value = HandleStore.globalConstants[id] + this.set(id, new Handle(env, id, value)) + this._map.set(value, [id]) + Reference.create(env, id, 1, false) + }) + } - public override add (h: Handle): void { - super.add(h) - const isRefType = isReferenceType(h.value) - try { - this.tryAddToMap(h, isRefType) - } catch (err) { - super.remove(h.id) - throw err - } - if (isRefType) { - if (this._objWeakMap.has(h.value)) { - const old = this._objWeakMap.get(h.value)! - old.moveTo(h) - } - this._objWeakMap.set(h.value, h) + public override add (h: Handle): void { + super.add(h) + const isRefType = isReferenceType(h.value) + try { + this.tryAddToMap(h, isRefType) + } catch (err) { + super.remove(h.id) + throw err + } + if (isRefType) { + if (this._objWeakMap.has(h.value)) { + const old = this._objWeakMap.get(h.value)! + old.moveTo(h) } + this._objWeakMap.set(h.value, h) } + } - public tryAddToMap (h: Handle, isRefType?: boolean): void { - isRefType = typeof isRefType === 'boolean' ? isRefType : isReferenceType(h.value) - if (this._map.has(h.value)) { - if (isRefType) { - throw new Error('An object is added to store twice') - } - const idArray = this._map.get(h.value)! - if (idArray.indexOf(h.id) === -1) { - idArray.push(h.id) - } - } else { - this._map.set(h.value, [h.id]) + public tryAddToMap (h: Handle, isRefType?: boolean): void { + isRefType = typeof isRefType === 'boolean' ? isRefType : isReferenceType(h.value) + if (this._map.has(h.value)) { + if (isRefType) { + throw new Error('An object is added to store twice') } + const idArray = this._map.get(h.value)! + if (idArray.indexOf(h.id) === -1) { + idArray.push(h.id) + } + } else { + this._map.set(h.value, [h.id]) } + } - public override remove (id: number): void { - if (!this.has(id) || id < this._min) return - const value = this.get(id)!.value - const idArray = this._map.get(value)! - const index = idArray.indexOf(id) - if (index !== -1) idArray.splice(index, 1) - if (idArray.length === 0) this._map.delete(value) - super.remove(id) - } - - public getObjectHandleExistsInStore (value: T): Handle | null { - const idArray = this._map.get(value) - return (idArray && idArray.length > 0) ? this.get(idArray[0])! : null - } + public override remove (id: number): void { + if (!this.has(id) || id < this._min) return + const value = this.get(id)!.value + const idArray = this._map.get(value)! + const index = idArray.indexOf(id) + if (index !== -1) idArray.splice(index, 1) + if (idArray.length === 0) this._map.delete(value) + super.remove(id) + } - public getObjectHandleAlive (value: T): Handle | null { - return (this._objWeakMap.get(value) as Handle) ?? null - } + public getObjectHandleExistsInStore (value: T): Handle | null { + const idArray = this._map.get(value) + return (idArray && idArray.length > 0) ? this.get(idArray[0])! : null + } - public dispose (): void { - this._objWeakMap = null! - this._map.forEach(arr => { arr.length = 0 }) - this._map.clear() - this._map = null! - super.dispose() - } + public getObjectHandleAlive (value: T): Handle | null { + return (this._objWeakMap.get(value) as Handle) ?? null } - export class Handle implements IStoreValue { - public static create (env: napi_env, value: S): Handle { - const handle = new Handle(env, 0, value) - envStore.get(env)!.handleStore.add(handle) - return handle - } + public dispose (): void { + this._objWeakMap = null! + this._map.forEach(arr => { arr.length = 0 }) + this._map.clear() + this._map = null! + super.dispose() + } +} - public id: number - public env: napi_env - public value: S - public inScope: IHandleScope | null - public wrapped: number = 0 // wrapped Reference id - public tag: [number, number, number, number] | null - public refs: Reference[] - - public constructor (env: napi_env, id: number, value: S) { - this.env = env - this.id = id - this.value = value - this.inScope = null - this.wrapped = 0 - this.tag = null - this.refs = [] - } +export class Handle implements IStoreValue { + public static create (env: napi_env, value: S): Handle { + const handle = new Handle(env, 0, value) + envStore.get(env)!.handleStore.add(handle) + return handle + } - public moveTo (other: Handle): void { - // other.env = this.env - this.env = 0 - // other.id = this.id - this.id = 0 - // other.value = this.value - this.value = undefined! - // other.inScope = this.inScope - this.inScope = null - other.wrapped = this.wrapped - this.wrapped = 0 - other.tag = this.tag - this.tag = null - other.refs = this.refs.slice() - this.refs.length = 0 - } + public id: number + public env: napi_env + public value: S + public inScope: IHandleScope | null + public wrapped: number = 0 // wrapped Reference id + public tag: [number, number, number, number] | null + public refs: Reference[] + + public constructor (env: napi_env, id: number, value: S) { + this.env = env + this.id = id + this.value = value + this.inScope = null + this.wrapped = 0 + this.tag = null + this.refs = [] + } - public isEmpty (): boolean { - return this.id === 0 - } + public moveTo (other: Handle): void { + // other.env = this.env + this.env = 0 + // other.id = this.id + this.id = 0 + // other.value = this.value + this.value = undefined! + // other.inScope = this.inScope + this.inScope = null + other.wrapped = this.wrapped + this.wrapped = 0 + other.tag = this.tag + this.tag = null + other.refs = this.refs.slice() + this.refs.length = 0 + } - public isNumber (): boolean { - return !this.isEmpty() && typeof this.value === 'number' - } + public isEmpty (): boolean { + return this.id === 0 + } - public isBigInt (): boolean { - return !this.isEmpty() && typeof this.value === 'bigint' - } + public isNumber (): boolean { + return !this.isEmpty() && typeof this.value === 'number' + } - public isString (): boolean { - return !this.isEmpty() && typeof this.value === 'string' - } + public isBigInt (): boolean { + return !this.isEmpty() && typeof this.value === 'bigint' + } - public isFunction (): boolean { - return !this.isEmpty() && typeof this.value === 'function' - } + public isString (): boolean { + return !this.isEmpty() && typeof this.value === 'string' + } - public isExternal (): boolean { - return !this.isEmpty() && (this instanceof ExternalHandle) - } + public isFunction (): boolean { + return !this.isEmpty() && typeof this.value === 'function' + } - public isObject (): boolean { - return !this.isEmpty() && typeof this.value === 'object' && this.value !== null - } + public isExternal (): boolean { + return !this.isEmpty() && (this instanceof ExternalHandle) + } - public isArray (): boolean { - return !this.isEmpty() && Array.isArray(this.value) - } + public isObject (): boolean { + return !this.isEmpty() && typeof this.value === 'object' && this.value !== null + } - public isArrayBuffer (): boolean { - return !this.isEmpty() && (this.value instanceof ArrayBuffer) - } + public isArray (): boolean { + return !this.isEmpty() && Array.isArray(this.value) + } - public isTypedArray (): boolean { - return !this.isEmpty() && (ArrayBuffer.isView(this.value)) && !(this.value instanceof DataView) - } + public isArrayBuffer (): boolean { + return !this.isEmpty() && (this.value instanceof ArrayBuffer) + } - public isDataView (): boolean { - return !this.isEmpty() && (this.value instanceof DataView) - } + public isTypedArray (): boolean { + return !this.isEmpty() && (ArrayBuffer.isView(this.value)) && !(this.value instanceof DataView) + } - public isDate (): boolean { - return !this.isEmpty() && (this.value instanceof Date) - } + public isDataView (): boolean { + return !this.isEmpty() && (this.value instanceof DataView) + } - public isPromise (): boolean { - return !this.isEmpty() && (this.value instanceof Promise) - } + public isDate (): boolean { + return !this.isEmpty() && (this.value instanceof Date) + } - public isBoolean (): boolean { - return !this.isEmpty() && typeof this.value === 'boolean' - } + public isPromise (): boolean { + return !this.isEmpty() && (this.value instanceof Promise) + } - public isUndefined (): boolean { - return !this.isEmpty() && this.value === undefined - } + public isBoolean (): boolean { + return !this.isEmpty() && typeof this.value === 'boolean' + } - public isSymbol (): boolean { - return !this.isEmpty() && typeof this.value === 'symbol' - } + public isUndefined (): boolean { + return !this.isEmpty() && this.value === undefined + } - public isNull (): boolean { - return !this.isEmpty() && this.value === null - } + public isSymbol (): boolean { + return !this.isEmpty() && typeof this.value === 'symbol' + } - public addRef (ref: Reference): void { - if (this.refs.indexOf(ref) !== -1) { - return - } - this.refs.push(ref) - } + public isNull (): boolean { + return !this.isEmpty() && this.value === null + } - public removeRef (ref: Reference): void { - const index = this.refs.indexOf(ref) - if (index !== -1) { - this.refs.splice(index, 1) - } - this.tryDispose() + public addRef (ref: Reference): void { + if (this.refs.indexOf(ref) !== -1) { + return } + this.refs.push(ref) + } - public isInHandleScope (): boolean { - return this.inScope !== null + public removeRef (ref: Reference): void { + const index = this.refs.indexOf(ref) + if (index !== -1) { + this.refs.splice(index, 1) } + this.tryDispose() + } - public tryDispose (): void { - if (this.canDispose()) { - this.dispose() - } - } + public isInHandleScope (): boolean { + return this.inScope !== null + } - public canDispose (): boolean { - return (this.id >= HandleStore.getMinId) && - ((this.refs.length === 0) || (!this.refs.some(ref => ref.refcount > 0))) && - (!this.isInHandleScope()) + public tryDispose (): void { + if (this.canDispose()) { + this.dispose() } + } - public dispose (): void { - if (this.id === 0) return - const refs = this.refs.slice() - for (let i = 0; i < refs.length; i++) { - const ref = refs[i] - ref.queueFinalizer() - } - const id = this.id - envStore.get(this.env)!.handleStore.remove(id) - this.refs.length = 0 - this.id = 0 - this.value = undefined! - } + public canDispose (): boolean { + return (this.id >= HandleStore.getMinId) && + ((this.refs.length === 0) || (!this.refs.some(ref => ref.refcount > 0))) && + (!this.isInHandleScope()) } - function External (this: any): void { - Object.setPrototypeOf(this, null) + public dispose (): void { + if (this.id === 0) return + const refs = this.refs.slice() + for (let i = 0; i < refs.length; i++) { + const ref = refs[i] + ref.queueFinalizer() + } + const id = this.id + envStore.get(this.env)!.handleStore.remove(id) + this.refs.length = 0 + this.id = 0 + this.value = undefined! } - External.prototype = null as any +} - export class ExternalHandle extends Handle<{}> { - public static createExternal (env: napi_env, data: void_p = 0): ExternalHandle { - const h = new ExternalHandle(env, data) - envStore.get(env)!.handleStore.add(h) - return h - } +function External (this: any): void { + Object.setPrototypeOf(this, null) +} +External.prototype = null as any - private readonly _data: void_p +export class ExternalHandle extends Handle<{}> { + public static createExternal (env: napi_env, data: void_p = 0): ExternalHandle { + const h = new ExternalHandle(env, data) + envStore.get(env)!.handleStore.add(h) + return h + } - public constructor (env: napi_env, data: void_p = 0) { - super(env, 0, new (External as any)()) - this._data = data - } + private readonly _data: void_p - public data (): void_p { - return this._data - } + public constructor (env: napi_env, data: void_p = 0) { + super(env, 0, new (External as any)()) + this._data = data + } + + public data (): void_p { + return this._data } } diff --git a/lib/runtime/HandleScope.ts b/lib/runtime/HandleScope.ts index 27d62117..860e24b1 100644 --- a/lib/runtime/HandleScope.ts +++ b/lib/runtime/HandleScope.ts @@ -1,151 +1,152 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export interface IHandleScope extends IStoreValue { - env: napi_env - parent: IHandleScope | null - child: IHandleScope | null - handles: Array> - add (value: V): Handle - addHandle> (handle: H): H - dispose (): void - } +import { Handle, HandleStore } from './Handle' +import { IStoreValue, Store } from './Store' +import { envStore, _global } from './util' - export class HandleScope implements IHandleScope { - public env: napi_env - public id: number - public parent: IHandleScope | null - public child: IHandleScope | null - public handles: Array> - private _disposed: boolean = false - - protected static _create (this: T, env: napi_env, parentScope: IHandleScope | null): InstanceType { - const scope = new this(env, parentScope) - if (parentScope) { - parentScope.child = scope - } - envStore.get(env)!.scopeStore.add(scope) - return scope as InstanceType - } +export interface IHandleScope extends IStoreValue { + env: napi_env + parent: IHandleScope | null + child: IHandleScope | null + handles: Array> + add (value: V): Handle + addHandle> (handle: H): H + dispose (): void +} - public static create (env: napi_env, parentScope: IHandleScope | null): HandleScope { - return HandleScope._create(env, parentScope) - } +export class HandleScope implements IHandleScope { + public env: napi_env + public id: number + public parent: IHandleScope | null + public child: IHandleScope | null + public handles: Array> + private _disposed: boolean = false - public constructor (env: napi_env, parentScope: IHandleScope | null) { - this.env = env - this.id = 0 - this.parent = parentScope - this.child = null - this.handles = [] + protected static _create (this: T, env: napi_env, parentScope: IHandleScope | null): InstanceType { + const scope = new this(env, parentScope) + if (parentScope) { + parentScope.child = scope } + envStore.get(env)!.scopeStore.add(scope) + return scope as InstanceType + } - public add (value: V): Handle { - if (value instanceof Handle) { - throw new TypeError('Can not add a handle to scope') - } + public static create (env: napi_env, parentScope: IHandleScope | null): HandleScope { + return HandleScope._create(env, parentScope) + } - if (value === undefined) { - return envStore.get(this.env)!.handleStore.get(HandleStore.ID_UNDEFINED)! - } - if (value === null) { - return envStore.get(this.env)!.handleStore.get(HandleStore.ID_NULL)! - } - if (typeof value === 'boolean') { - return envStore.get(this.env)!.handleStore.get(value ? HandleStore.ID_TRUE : HandleStore.ID_FALSE)! - } - if (value === _global) { - return envStore.get(this.env)!.handleStore.get(HandleStore.ID_GLOBAL)! - } + public constructor (env: napi_env, parentScope: IHandleScope | null) { + this.env = env + this.id = 0 + this.parent = parentScope + this.child = null + this.handles = [] + } - const h = Handle.create(this.env, value) - this.handles.push(h) - h.inScope = this - return h + public add (value: V): Handle { + if (value instanceof Handle) { + throw new TypeError('Can not add a handle to scope') } - public addHandle> (handle: H): H { - if (this.handles.indexOf(handle) !== -1) { - return handle - } - this.handles.push(handle) - handle.inScope = this - return handle + if (value === undefined) { + return envStore.get(this.env)!.handleStore.get(HandleStore.ID_UNDEFINED)! + } + if (value === null) { + return envStore.get(this.env)!.handleStore.get(HandleStore.ID_NULL)! + } + if (typeof value === 'boolean') { + return envStore.get(this.env)!.handleStore.get(value ? HandleStore.ID_TRUE : HandleStore.ID_FALSE)! + } + if (value === _global) { + return envStore.get(this.env)!.handleStore.get(HandleStore.ID_GLOBAL)! } - public dispose (): void { - if (this._disposed) return - this._disposed = true - const handles = this.handles.slice() - for (let i = 0; i < handles.length; i++) { - const handle = handles[i] - handle.inScope = null - handle.tryDispose() - } - this.handles.length = 0 - if (this.parent) { - this.parent.child = null - } - this.parent = null - envStore.get(this.env)!.scopeStore.remove(this.id) + const h = Handle.create(this.env, value) + this.handles.push(h) + h.inScope = this + return h + } + + public addHandle> (handle: H): H { + if (this.handles.indexOf(handle) !== -1) { + return handle } + this.handles.push(handle) + handle.inScope = this + return handle } - export class EscapableHandleScope extends HandleScope { - public static create (env: napi_env, parentScope: IHandleScope | null): EscapableHandleScope { - return EscapableHandleScope._create(env, parentScope) + public dispose (): void { + if (this._disposed) return + this._disposed = true + const handles = this.handles.slice() + for (let i = 0; i < handles.length; i++) { + const handle = handles[i] + handle.inScope = null + handle.tryDispose() + } + this.handles.length = 0 + if (this.parent) { + this.parent.child = null } + this.parent = null + envStore.get(this.env)!.scopeStore.remove(this.id) + } +} - private _escapeCalled: boolean +export class EscapableHandleScope extends HandleScope { + public static create (env: napi_env, parentScope: IHandleScope | null): EscapableHandleScope { + return EscapableHandleScope._create(env, parentScope) + } - public constructor (public env: napi_env, parentScope: IHandleScope | null) { - super(env, parentScope) - this._escapeCalled = false - } + private _escapeCalled: boolean - public escape (handle: number | Handle): Handle | null { - if (this._escapeCalled) return null - this._escapeCalled = true - let exists: boolean = false - let index: number = -1 - let handleId: number - if (typeof handle === 'number') { - handleId = handle - for (let i = 0; i < this.handles.length; i++) { - if (this.handles[i].id === handleId) { - index = i - exists = true - break - } + public constructor (public env: napi_env, parentScope: IHandleScope | null) { + super(env, parentScope) + this._escapeCalled = false + } + + public escape (handle: number | Handle): Handle | null { + if (this._escapeCalled) return null + this._escapeCalled = true + let exists: boolean = false + let index: number = -1 + let handleId: number + if (typeof handle === 'number') { + handleId = handle + for (let i = 0; i < this.handles.length; i++) { + if (this.handles[i].id === handleId) { + index = i + exists = true + break } - } else { - handleId = handle.id - index = this.handles.indexOf(handle) - exists = index !== -1 } - if (exists) { - const envObject = envStore.get(this.env)! - const h = envObject.handleStore.get(handleId) - if (h && this.parent !== null) { - this.handles.splice(index, 1) - envObject.handleStore.remove(handleId) - const newHandle = this.parent.add(h.value) - return newHandle - } else { - return null - } + } else { + handleId = handle.id + index = this.handles.indexOf(handle) + exists = index !== -1 + } + if (exists) { + const envObject = envStore.get(this.env)! + const h = envObject.handleStore.get(handleId) + if (h && this.parent !== null) { + this.handles.splice(index, 1) + envObject.handleStore.remove(handleId) + const newHandle = this.parent.add(h.value) + return newHandle } else { return null } + } else { + return null } + } - public escapeCalled (): boolean { - return this._escapeCalled - } + public escapeCalled (): boolean { + return this._escapeCalled } +} - export class ScopeStore extends Store { - public constructor () { - super() - } +export class ScopeStore extends Store { + public constructor () { + super() } } diff --git a/lib/runtime/LinkedList.ts b/lib/runtime/LinkedList.ts index bdc45b1a..8361d894 100644 --- a/lib/runtime/LinkedList.ts +++ b/lib/runtime/LinkedList.ts @@ -1,119 +1,115 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export class ListNode { - static readonly Undefined = new ListNode(undefined) +export class ListNode { + static readonly Undefined = new ListNode(undefined) - element: E - next: ListNode - prev: ListNode + element: E + next: ListNode + prev: ListNode - constructor (element: E) { - this.element = element - this.next = ListNode.Undefined - this.prev = ListNode.Undefined - } + constructor (element: E) { + this.element = element + this.next = ListNode.Undefined + this.prev = ListNode.Undefined } +} - export class LinkedList { - first: ListNode = ListNode.Undefined - last: ListNode = ListNode.Undefined - - private _size: number = 0 +export class LinkedList { + first: ListNode = ListNode.Undefined + last: ListNode = ListNode.Undefined - get size (): number { - return this._size - } + private _size: number = 0 - isEmpty (): boolean { - return this.first === ListNode.Undefined - } + get size (): number { + return this._size + } - clear (): void { - this.first = ListNode.Undefined - this.last = ListNode.Undefined - this._size = 0 - } + isEmpty (): boolean { + return this.first === ListNode.Undefined + } - unshift (element: E): () => void { - return this._insert(element, false) - } + clear (): void { + this.first = ListNode.Undefined + this.last = ListNode.Undefined + this._size = 0 + } - push (element: E): () => void { - return this._insert(element, true) - } + unshift (element: E): () => void { + return this._insert(element, false) + } - private _insert (element: E, atTheEnd: boolean): () => void { - const newNode = new ListNode(element) - if (this.first === ListNode.Undefined) { - this.first = newNode - this.last = newNode - } else if (atTheEnd) { - // push - const oldLast = this.last - this.last = newNode - newNode.prev = oldLast - oldLast.next = newNode - } else { - // unshift - const oldFirst = this.first - this.first = newNode - newNode.next = oldFirst - oldFirst.prev = newNode - } - this._size += 1 + push (element: E): () => void { + return this._insert(element, true) + } - let didRemove = false - return () => { - if (!didRemove) { - didRemove = true - this._remove(newNode) - } - } + private _insert (element: E, atTheEnd: boolean): () => void { + const newNode = new ListNode(element) + if (this.first === ListNode.Undefined) { + this.first = newNode + this.last = newNode + } else if (atTheEnd) { + // push + const oldLast = this.last + this.last = newNode + newNode.prev = oldLast + oldLast.next = newNode + } else { + // unshift + const oldFirst = this.first + this.first = newNode + newNode.next = oldFirst + oldFirst.prev = newNode } + this._size += 1 - shift (): E | undefined { - if (this.first === ListNode.Undefined) { - return undefined - } else { - const res = this.first.element - this._remove(this.first) - return res + let didRemove = false + return () => { + if (!didRemove) { + didRemove = true + this._remove(newNode) } } + } - pop (): E | undefined { - if (this.last === ListNode.Undefined) { - return undefined - } else { - const res = this.last.element - this._remove(this.last) - return res - } + shift (): E | undefined { + if (this.first === ListNode.Undefined) { + return undefined + } else { + const res = this.first.element + this._remove(this.first) + return res } + } - private _remove (node: ListNode): void { - if (node.prev !== ListNode.Undefined && node.next !== ListNode.Undefined) { - // middle - const anchor = node.prev - anchor.next = node.next - node.next.prev = anchor - } else if (node.prev === ListNode.Undefined && node.next === ListNode.Undefined) { - // only node - this.first = ListNode.Undefined - this.last = ListNode.Undefined - } else if (node.next === ListNode.Undefined) { - // last - this.last = this.last.prev! - this.last.next = ListNode.Undefined - } else if (node.prev === ListNode.Undefined) { - // first - this.first = this.first.next! - this.first.prev = ListNode.Undefined - } - - // done - this._size -= 1 + pop (): E | undefined { + if (this.last === ListNode.Undefined) { + return undefined + } else { + const res = this.last.element + this._remove(this.last) + return res } } + private _remove (node: ListNode): void { + if (node.prev !== ListNode.Undefined && node.next !== ListNode.Undefined) { + // middle + const anchor = node.prev + anchor.next = node.next + node.next.prev = anchor + } else if (node.prev === ListNode.Undefined && node.next === ListNode.Undefined) { + // only node + this.first = ListNode.Undefined + this.last = ListNode.Undefined + } else if (node.next === ListNode.Undefined) { + // last + this.last = this.last.prev! + this.last.next = ListNode.Undefined + } else if (node.prev === ListNode.Undefined) { + // first + this.first = this.first.next! + this.first.prev = ListNode.Undefined + } + + // done + this._size -= 1 + } } diff --git a/lib/runtime/Reference.ts b/lib/runtime/Reference.ts index 9f123f9b..814be3cf 100644 --- a/lib/runtime/Reference.ts +++ b/lib/runtime/Reference.ts @@ -1,148 +1,148 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export class Reference implements IStoreValue { - public id: number - public refcount: uint32_t +import { IStoreValue, Store } from './Store' +import { NULL, envStore, supportFinalizer, isReferenceType } from './util' - private finalizeRan: boolean = false +export class Reference implements IStoreValue { + public id: number + public refcount: uint32_t - private finalizerRegistered: boolean = false + private finalizeRan: boolean = false - public static finalizationGroup: FinalizationRegistry | null = - typeof FinalizationRegistry !== 'undefined' - ? new FinalizationRegistry((ref: Reference) => { - let error: any - let caught = false - if (ref.finalize_callback !== NULL) { - try { - envStore.get(ref.env)!.callIntoModule((envObject) => { - envObject.call_viii(ref.finalize_callback, ref.env, ref.finalize_data, ref.finalize_hint) - ref.finalize_callback = NULL - }) - } catch (err) { - caught = true - error = err - } - } - if (ref.deleteSelf) { - Reference.doDelete(ref) - } else { - ref.finalizeRan = true - // leak if this is a non-self-delete weak reference - // should call napi_delete_referece manually - // Reference.doDelete(this) - } - if (caught) { - throw error + private finalizerRegistered: boolean = false + + public static finalizationGroup: FinalizationRegistry | null = + typeof FinalizationRegistry !== 'undefined' + ? new FinalizationRegistry((ref: Reference) => { + let error: any + let caught = false + if (ref.finalize_callback !== NULL) { + try { + envStore.get(ref.env)!.callIntoModule((envObject) => { + envObject.call_viii(ref.finalize_callback, ref.env, ref.finalize_data, ref.finalize_hint) + ref.finalize_callback = NULL + }) + } catch (err) { + caught = true + error = err } - }) - : null + } + if (ref.deleteSelf) { + Reference.doDelete(ref) + } else { + ref.finalizeRan = true + // leak if this is a non-self-delete weak reference + // should call napi_delete_referece manually + // Reference.doDelete(this) + } + if (caught) { + throw error + } + }) + : null - public static create ( - env: napi_env, - handle_id: napi_value, - initialRefcount: uint32_t, - deleteSelf: boolean, - finalize_callback: napi_finalize = 0, - finalize_data: void_p = 0, - finalize_hint: void_p = 0 - ): Reference { - const ref = new Reference(env, handle_id, initialRefcount, deleteSelf, finalize_callback, finalize_data, finalize_hint) - const envObject = envStore.get(env)! - envObject.refStore.add(ref) - const handle = envObject.handleStore.get(handle_id)! - handle.addRef(ref) - if (supportFinalizer && isReferenceType(handle.value)) { - ref.objWeakRef = new WeakRef(handle.value) - } else { - ref.objWeakRef = null - } - return ref + public static create ( + env: napi_env, + handle_id: napi_value, + initialRefcount: uint32_t, + deleteSelf: boolean, + finalize_callback: napi_finalize = 0, + finalize_data: void_p = 0, + finalize_hint: void_p = 0 + ): Reference { + const ref = new Reference(env, handle_id, initialRefcount, deleteSelf, finalize_callback, finalize_data, finalize_hint) + const envObject = envStore.get(env)! + envObject.refStore.add(ref) + const handle = envObject.handleStore.get(handle_id)! + handle.addRef(ref) + if (supportFinalizer && isReferenceType(handle.value)) { + ref.objWeakRef = new WeakRef(handle.value) + } else { + ref.objWeakRef = null } + return ref + } - public objWeakRef!: WeakRef | null + public objWeakRef!: WeakRef | null - private constructor ( - public env: napi_env, - public handle_id: napi_value, - initialRefcount: uint32_t, - public deleteSelf: boolean, - public finalize_callback: napi_finalize = 0, - public finalize_data: void_p = 0, - public finalize_hint: void_p = 0 - ) { - this.id = 0 - this.refcount = initialRefcount >>> 0 - } + private constructor ( + public env: napi_env, + public handle_id: napi_value, + initialRefcount: uint32_t, + public deleteSelf: boolean, + public finalize_callback: napi_finalize = 0, + public finalize_data: void_p = 0, + public finalize_hint: void_p = 0 + ) { + this.id = 0 + this.refcount = initialRefcount >>> 0 + } - public ref (): number { - return ++this.refcount - } + public ref (): number { + return ++this.refcount + } - public unref (): number { - if (this.refcount === 0) { - return 0 - } - this.refcount-- - if (this.refcount === 0) { - const envObject = envStore.get(this.env)! - const handle = envObject.handleStore.get(this.handle_id) - if (handle) { - handle.tryDispose() - } + public unref (): number { + if (this.refcount === 0) { + return 0 + } + this.refcount-- + if (this.refcount === 0) { + const envObject = envStore.get(this.env)! + const handle = envObject.handleStore.get(this.handle_id) + if (handle) { + handle.tryDispose() } - return this.refcount } + return this.refcount + } - public data (): void_p { - return this.finalize_data - } + public data (): void_p { + return this.finalize_data + } - public get (): napi_value { - const envObject = envStore.get(this.env)! - if (envObject.handleStore.has(this.handle_id)) { - return this.handle_id - } else { - if (this.objWeakRef) { - const obj = this.objWeakRef.deref() - if (obj) { - this.handle_id = envObject.ensureHandleId(obj) - return this.handle_id - } + public get (): napi_value { + const envObject = envStore.get(this.env)! + if (envObject.handleStore.has(this.handle_id)) { + return this.handle_id + } else { + if (this.objWeakRef) { + const obj = this.objWeakRef.deref() + if (obj) { + this.handle_id = envObject.ensureHandleId(obj) + return this.handle_id } - return NULL } + return NULL } + } - public static doDelete (ref: Reference): void { - if ((ref.refcount !== 0) || (ref.deleteSelf) || (ref.finalizeRan)) { - const envObject = envStore.get(ref.env)! - envObject.refStore.remove(ref.id) - envObject.handleStore.get(ref.handle_id)?.removeRef(ref) - Reference.finalizationGroup?.unregister(this) - } else { - ref.deleteSelf = true - } + public static doDelete (ref: Reference): void { + if ((ref.refcount !== 0) || (ref.deleteSelf) || (ref.finalizeRan)) { + const envObject = envStore.get(ref.env)! + envObject.refStore.remove(ref.id) + envObject.handleStore.get(ref.handle_id)?.removeRef(ref) + Reference.finalizationGroup?.unregister(this) + } else { + ref.deleteSelf = true } + } - public queueFinalizer (): void { - if (!Reference.finalizationGroup) return - if (this.finalizerRegistered) return - const envObject = envStore.get(this.env)! - const handle = envObject.handleStore.get(this.handle_id)! - Reference.finalizationGroup.register(handle.value, this, this) - this.finalizerRegistered = true - } + public queueFinalizer (): void { + if (!Reference.finalizationGroup) return + if (this.finalizerRegistered) return + const envObject = envStore.get(this.env)! + const handle = envObject.handleStore.get(this.handle_id)! + Reference.finalizationGroup.register(handle.value, this, this) + this.finalizerRegistered = true + } - public dispose (): void { - this.deleteSelf = true - Reference.doDelete(this) - } + public dispose (): void { + this.deleteSelf = true + Reference.doDelete(this) } +} - export class RefStore extends Store { - public constructor () { - super() - } +export class RefStore extends Store { + public constructor () { + super() } } diff --git a/lib/runtime/Store.ts b/lib/runtime/Store.ts index 1cbe002c..6ea157ad 100644 --- a/lib/runtime/Store.ts +++ b/lib/runtime/Store.ts @@ -1,77 +1,72 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - - export interface IStoreValue { - id: number - dispose (): void - [x: string]: any - } +export interface IStoreValue { + id: number + dispose (): void + [x: string]: any +} - export class Store { - protected readonly _values: { [id: number]: V } - protected _min: number +export class Store { + protected readonly _values: { [id: number]: V } + protected _min: number - // -2147483648 <= _id <= 2147483647 && _id !== 0 - private _id: number + // -2147483648 <= _id <= 2147483647 && _id !== 0 + private _id: number - public constructor (min = -2147483648) { - this._values = Object.create(null) - this._min = min - this._id = min - } + public constructor (min = -2147483648) { + this._values = Object.create(null) + this._min = min + this._id = min + } - private _nextId (): void { - this._id = (this._id === 2147483647 ? this._min : (this._id + 1)) || 1 - } + private _nextId (): void { + this._id = (this._id === 2147483647 ? this._min : (this._id + 1)) || 1 + } - public add (value: V): void { - while (this._id in this._values) { - this._nextId() - } - value.id = this._id - this._values[this._id] = value + public add (value: V): void { + while (this._id in this._values) { this._nextId() } + value.id = this._id + this._values[this._id] = value + this._nextId() + } - public get (id: number): V | undefined { - return this._values[id] - } - - protected set (id: number, value: V): void { - this._values[id] = value - value.id = id - } + public get (id: number): V | undefined { + return this._values[id] + } - public has (id: number): boolean { - return id in this._values - } + protected set (id: number, value: V): void { + this._values[id] = value + value.id = id + } - public remove (id: number): void { - if (id in this._values) { - this._values[id].id = 0 - delete this._values[id] - } - } + public has (id: number): boolean { + return id in this._values + } - public forEach (fn: (this: any, value: V, id: number, store: Store) => void, thisArg?: any): void { - Object.keys(this._values).forEach((value) => { - const _id = Number(value) - fn.call(thisArg, this._values[_id], _id, this) - }) + public remove (id: number): void { + if (id in this._values) { + this._values[id].id = 0 + delete this._values[id] } + } - public allId (): number[] { - return Object.keys(this._values).map(Number) - } + public forEach (fn: (this: any, value: V, id: number, store: Store) => void, thisArg?: any): void { + Object.keys(this._values).forEach((value) => { + const _id = Number(value) + fn.call(thisArg, this._values[_id], _id, this) + }) + } - public dispose (): void { - Object.keys(this._values).forEach((k) => { - try { - this._values[k as any].dispose() - } catch (_) {} - delete this._values[k as any] - }) - } + public allId (): number[] { + return Object.keys(this._values).map(Number) } + public dispose (): void { + Object.keys(this._values).forEach((k) => { + try { + this._values[k as any].dispose() + } catch (_) {} + delete this._values[k as any] + }) + } } diff --git a/lib/runtime/api-extractor.json b/lib/runtime/api-extractor.json new file mode 100644 index 00000000..bd585884 --- /dev/null +++ b/lib/runtime/api-extractor.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + "projectFolder": "../..", + + "mainEntryPointFilePath": "/dist/runtime/index.d.ts", + + "bundledPackages": [], + + "compiler": { + "tsconfigFilePath": "/lib/runtime/tsconfig.json" + + // "overrideTsconfig": { + // . . . + // } + + // "skipLibCheck": true, + }, + + "apiReport": { + "enabled": false + + // "reportFileName": ".api.md", + + // "reportFolder": "/etc/", + + // "reportTempFolder": "/api/temp/" + }, + + "docModel": { + "enabled": false + + // "apiJsonFilePath": "/temp/.api.json" + }, + + "dtsRollup": { + "enabled": true, + + "untrimmedFilePath": "", + + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + "publicTrimmedFilePath": "/dist/library_napi_runtime.d.ts" + + // "omitTrimmingComments": true + }, + + "tsdocMetadata": { + "enabled": false, + + "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + // "newlineKind": "crlf", + + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + "default": { + "logLevel": "warning" + + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + "ae-missing-release-tag": { + "logLevel": "none", + "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/lib/runtime/env.ts b/lib/runtime/env.ts index d2592338..d2eb91f2 100644 --- a/lib/runtime/env.ts +++ b/lib/runtime/env.ts @@ -1,232 +1,238 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export class Env implements IStoreValue { - public id: number - - typedArrayMemoryMap = new WeakMap() - arrayBufferMemoryMap = new WeakMap() - memoryPointerDeleter: FinalizationRegistry = supportFinalizer - ? new FinalizationRegistry((heldValue) => { - this.free(heldValue) - }) - : null! - - public openHandleScopes: number = 0 - - public instanceData = { - data: 0, - finalize_cb: 0, - finalize_hint: 0 - } +import { DeferredStore } from './Deferred' +import { HandleStore } from './Handle' +import { ScopeStore, IHandleScope, HandleScope, EscapableHandleScope } from './HandleScope' +import { LinkedList } from './LinkedList' +import { RefStore } from './Reference' +import { IStoreValue, Store } from './Store' +import { napi_status } from './type' +import { TypedArray, supportFinalizer, NULL, TryCatch, envStore, isReferenceType } from './util' + +export class Env implements IStoreValue { + public id: number + + typedArrayMemoryMap = new WeakMap() + arrayBufferMemoryMap = new WeakMap() + memoryPointerDeleter: FinalizationRegistry = supportFinalizer + ? new FinalizationRegistry((heldValue) => { + this.free(heldValue) + }) + : null! + + public openHandleScopes: number = 0 + + public instanceData = { + data: 0, + finalize_cb: 0, + finalize_hint: 0 + } - public handleStore!: HandleStore - public scopeStore!: ScopeStore - public refStore!: RefStore - public deferredStore!: DeferredStore + public handleStore!: HandleStore + public scopeStore!: ScopeStore + public refStore!: RefStore + public deferredStore!: DeferredStore - public scopeList = new LinkedList() + private scopeList = new LinkedList() - public napiExtendedErrorInfo = { - error_message: 0, - engine_reserved: 0, - engine_error_code: 0, - error_code: napi_status.napi_ok - } + public napiExtendedErrorInfo = { + error_message: 0, + engine_reserved: 0, + engine_error_code: 0, + error_code: napi_status.napi_ok + } - public napiExtendedErrorInfoPtr: Pointer = NULL - - public tryCatch = new TryCatch() - - public static create ( - malloc: (size: number) => number, - free: (ptr: number) => void, - call_iii: (ptr: number, ...args: [number, number]) => number, - call_viii: (ptr: number, ...args: [number, number, number]) => void, - HEAP32: Int32Array, - HEAPU32: Uint32Array, - HEAPU8: Uint8Array - ): Env { - const env = new Env(malloc, free, call_iii, call_viii, HEAP32, HEAPU32, HEAPU8) - envStore.add(env) - env.refStore = new RefStore() - env.handleStore = new HandleStore() - env.handleStore.addGlobalConstants(env.id) - env.deferredStore = new DeferredStore() - env.scopeStore = new ScopeStore() - env.scopeList = new LinkedList() - // env.scopeList.push(HandleScope.create(env.id, null)) - env.napiExtendedErrorInfoPtr = env.malloc(16) - return env - } + public napiExtendedErrorInfoPtr: Pointer = NULL + + public tryCatch = new TryCatch() + + public static create ( + malloc: (size: number) => number, + free: (ptr: number) => void, + call_iii: (ptr: number, ...args: [number, number]) => number, + call_viii: (ptr: number, ...args: [number, number, number]) => void, + HEAP32: Int32Array, + HEAPU32: Uint32Array, + HEAPU8: Uint8Array + ): Env { + const env = new Env(malloc, free, call_iii, call_viii, HEAP32, HEAPU32, HEAPU8) + envStore.add(env) + env.refStore = new RefStore() + env.handleStore = new HandleStore() + env.handleStore.addGlobalConstants(env.id) + env.deferredStore = new DeferredStore() + env.scopeStore = new ScopeStore() + env.scopeList = new LinkedList() + // env.scopeList.push(HandleScope.create(env.id, null)) + env.napiExtendedErrorInfoPtr = env.malloc(16) + return env + } - private constructor ( - public malloc: (size: number) => number, - public free: (ptr: number) => void, - public call_iii: (ptr: number, ...args: [number, number]) => number, - public call_viii: (ptr: number, ...args: [number, number, number]) => void, - public HEAP32: Int32Array, - public HEAPU32: Uint32Array, - public HEAPU8: Uint8Array - ) { - this.id = 0 - } + private constructor ( + public malloc: (size: number) => number, + public free: (ptr: number) => void, + public call_iii: (ptr: number, ...args: [number, number]) => number, + public call_viii: (ptr: number, ...args: [number, number, number]) => void, + public HEAP32: Int32Array, + public HEAPU32: Uint32Array, + public HEAPU8: Uint8Array + ) { + this.id = 0 + } - public openScope (ScopeConstructor: { create: (env: napi_env, parent: IHandleScope | null) => Scope }): Scope { - const scope = ScopeConstructor.create(this.id, this.getCurrentScope() ?? null) - this.scopeList.push(scope) - this.openHandleScopes++ - return scope - } + public openScope (ScopeConstructor: { create: (env: napi_env, parent: IHandleScope | null) => Scope }): Scope { + const scope = ScopeConstructor.create(this.id, this.getCurrentScope() ?? null) + this.scopeList.push(scope) + this.openHandleScopes++ + return scope + } - public closeScope (scope: IHandleScope): void { - scope.dispose() - this.scopeList.pop() - this.openHandleScopes-- - } + public closeScope (scope: IHandleScope): void { + scope.dispose() + this.scopeList.pop() + this.openHandleScopes-- + } - public callInNewScope ( - ScopeConstructor: { create: (env: napi_env, parent: IHandleScope | null) => Scope }, - fn: (scope: Scope, ...args: Args) => ReturnValue, - ...args: Args - ): ReturnValue { - const scope = this.openScope(ScopeConstructor) - let ret: ReturnValue - try { - ret = fn(scope, ...args) - } catch (err) { - this.tryCatch.setError(err) - } - this.closeScope(scope) - return ret! - } + public callInNewScope ( + ScopeConstructor: { create: (env: napi_env, parent: IHandleScope | null) => Scope }, + fn: (scope: Scope, ...args: Args) => ReturnValue, + ...args: Args + ): ReturnValue { + const scope = this.openScope(ScopeConstructor) + let ret: ReturnValue + try { + ret = fn(scope, ...args) + } catch (err) { + this.tryCatch.setError(err) + } + this.closeScope(scope) + return ret! + } - public callInNewHandleScope (fn: (scope: HandleScope, ...args: Args) => T, ...args: Args): T { - return this.callInNewScope(HandleScope, fn, ...args) - } + public callInNewHandleScope (fn: (scope: HandleScope, ...args: Args) => T, ...args: Args): T { + return this.callInNewScope(HandleScope, fn, ...args) + } - public callInNewEscapableHandleScope (fn: (scope: EscapableHandleScope, ...args: Args) => T, ...args: Args): T { - return this.callInNewScope(EscapableHandleScope, fn, ...args) - } + public callInNewEscapableHandleScope (fn: (scope: EscapableHandleScope, ...args: Args) => T, ...args: Args): T { + return this.callInNewScope(EscapableHandleScope, fn, ...args) + } - public getCurrentScope (): IHandleScope { - return this.scopeList.last.element - } + public getCurrentScope (): IHandleScope { + return this.scopeList.last.element + } - public ensureHandleId (value: any): napi_value { - if (isReferenceType(value)) { - let handle = this.handleStore.getObjectHandleExistsInStore(value) - if (handle) return handle.id - handle = this.handleStore.getObjectHandleAlive(value) - if (!handle) { - return this.getCurrentScope().add(value).id - } - if (handle.value === undefined) { - // should always true - const currentScope = this.getCurrentScope() - handle.value = value - Store.prototype.add.call(this.handleStore, handle) - this.handleStore.tryAddToMap(handle, true) - currentScope.addHandle(handle) - } - return handle.id + public ensureHandleId (value: any): napi_value { + if (isReferenceType(value)) { + let handle = this.handleStore.getObjectHandleExistsInStore(value) + if (handle) return handle.id + handle = this.handleStore.getObjectHandleAlive(value) + if (!handle) { + return this.getCurrentScope().add(value).id } - - return this.getCurrentScope().add(value).id + if (handle.value === undefined) { + // should always true + const currentScope = this.getCurrentScope() + handle.value = value + Store.prototype.add.call(this.handleStore, handle) + this.handleStore.tryAddToMap(handle, true) + currentScope.addHandle(handle) + } + return handle.id } - public clearLastError (): napi_status { - this.napiExtendedErrorInfo.error_code = napi_status.napi_ok - this.napiExtendedErrorInfo.engine_error_code = 0 - this.napiExtendedErrorInfo.engine_reserved = 0 + return this.getCurrentScope().add(value).id + } - const ptr32 = this.napiExtendedErrorInfoPtr >> 2 - this.HEAP32[ptr32 + 1] = this.napiExtendedErrorInfo.engine_reserved - this.HEAPU32[ptr32 + 2] = this.napiExtendedErrorInfo.engine_error_code - this.HEAP32[ptr32 + 3] = this.napiExtendedErrorInfo.error_code - return napi_status.napi_ok - } + public clearLastError (): napi_status { + this.napiExtendedErrorInfo.error_code = napi_status.napi_ok + this.napiExtendedErrorInfo.engine_error_code = 0 + this.napiExtendedErrorInfo.engine_reserved = 0 - public setLastError (error_code: napi_status, engine_error_code: uint32_t = 0, engine_reserved: void_p = 0): napi_status { - this.napiExtendedErrorInfo.error_code = error_code - this.napiExtendedErrorInfo.engine_error_code = engine_error_code - this.napiExtendedErrorInfo.engine_reserved = engine_reserved + const ptr32 = this.napiExtendedErrorInfoPtr >> 2 + this.HEAP32[ptr32 + 1] = this.napiExtendedErrorInfo.engine_reserved + this.HEAPU32[ptr32 + 2] = this.napiExtendedErrorInfo.engine_error_code + this.HEAP32[ptr32 + 3] = this.napiExtendedErrorInfo.error_code + return napi_status.napi_ok + } - const ptr32 = this.napiExtendedErrorInfoPtr >> 2 - this.HEAP32[ptr32 + 1] = this.napiExtendedErrorInfo.engine_reserved - this.HEAPU32[ptr32 + 2] = this.napiExtendedErrorInfo.engine_error_code - this.HEAP32[ptr32 + 3] = this.napiExtendedErrorInfo.error_code - return error_code - } + public setLastError (error_code: napi_status, engine_error_code: uint32_t = 0, engine_reserved: void_p = 0): napi_status { + this.napiExtendedErrorInfo.error_code = error_code + this.napiExtendedErrorInfo.engine_error_code = engine_error_code + this.napiExtendedErrorInfo.engine_reserved = engine_reserved - public getReturnStatus (): napi_status { - return !this.tryCatch.hasCaught() ? napi_status.napi_ok : this.setLastError(napi_status.napi_pending_exception) - } + const ptr32 = this.napiExtendedErrorInfoPtr >> 2 + this.HEAP32[ptr32 + 1] = this.napiExtendedErrorInfo.engine_reserved + this.HEAPU32[ptr32 + 2] = this.napiExtendedErrorInfo.engine_error_code + this.HEAP32[ptr32 + 3] = this.napiExtendedErrorInfo.error_code + return error_code + } - public callIntoModule (fn: (env: Env, scope: IHandleScope) => T): T { - const r = this.callInNewHandleScope((scope) => { - this.clearLastError() - return fn(this, scope) - }) - if (this.tryCatch.hasCaught()) { - const err = this.tryCatch.extractException()! - throw err - } - return r - } + public getReturnStatus (): napi_status { + return !this.tryCatch.hasCaught() ? napi_status.napi_ok : this.setLastError(napi_status.napi_pending_exception) + } - public getViewPointer (view: TypedArray | DataView): void_p { - if (!supportFinalizer) { - return NULL - } - if (view.buffer === this.HEAPU8.buffer) { - return view.byteOffset - } + public callIntoModule (fn: (env: Env, scope: IHandleScope) => T): T { + const r = this.callInNewHandleScope((scope) => { + this.clearLastError() + return fn(this, scope) + }) + if (this.tryCatch.hasCaught()) { + const err = this.tryCatch.extractException()! + throw err + } + return r + } - let pointer: void_p - if (this.typedArrayMemoryMap.has(view)) { - pointer = this.typedArrayMemoryMap.get(view)! - this.HEAPU8.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), pointer) - return pointer - } + public getViewPointer (view: TypedArray | DataView): void_p { + if (!supportFinalizer) { + return NULL + } + if (view.buffer === this.HEAPU8.buffer) { + return view.byteOffset + } - pointer = this.malloc(view.byteLength) + let pointer: void_p + if (this.typedArrayMemoryMap.has(view)) { + pointer = this.typedArrayMemoryMap.get(view)! this.HEAPU8.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), pointer) - this.typedArrayMemoryMap.set(view, pointer) - this.memoryPointerDeleter.register(view, pointer) return pointer } - public getArrayBufferPointer (arrayBuffer: ArrayBuffer): void_p { - if ((!supportFinalizer) || (arrayBuffer === this.HEAPU8.buffer)) { - return NULL - } + pointer = this.malloc(view.byteLength) + this.HEAPU8.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), pointer) + this.typedArrayMemoryMap.set(view, pointer) + this.memoryPointerDeleter.register(view, pointer) + return pointer + } - let pointer: void_p - if (this.arrayBufferMemoryMap.has(arrayBuffer)) { - pointer = this.arrayBufferMemoryMap.get(arrayBuffer)! - this.HEAPU8.set(new Uint8Array(arrayBuffer), pointer) - return pointer - } + public getArrayBufferPointer (arrayBuffer: ArrayBuffer): void_p { + if ((!supportFinalizer) || (arrayBuffer === this.HEAPU8.buffer)) { + return NULL + } - pointer = this.malloc(arrayBuffer.byteLength) + let pointer: void_p + if (this.arrayBufferMemoryMap.has(arrayBuffer)) { + pointer = this.arrayBufferMemoryMap.get(arrayBuffer)! this.HEAPU8.set(new Uint8Array(arrayBuffer), pointer) - this.arrayBufferMemoryMap.set(arrayBuffer, pointer) - this.memoryPointerDeleter.register(arrayBuffer, pointer) return pointer } - public dispose (): void { - this.scopeList.clear() - this.deferredStore.dispose() - this.refStore.dispose() - this.scopeStore.dispose() - this.handleStore.dispose() - this.tryCatch.extractException() - try { - this.free(this.napiExtendedErrorInfoPtr) - this.napiExtendedErrorInfoPtr = NULL - } catch (_) {} - envStore.remove(this.id) - } + pointer = this.malloc(arrayBuffer.byteLength) + this.HEAPU8.set(new Uint8Array(arrayBuffer), pointer) + this.arrayBufferMemoryMap.set(arrayBuffer, pointer) + this.memoryPointerDeleter.register(arrayBuffer, pointer) + return pointer + } + + public dispose (): void { + this.scopeList.clear() + this.deferredStore.dispose() + this.refStore.dispose() + this.scopeStore.dispose() + this.handleStore.dispose() + this.tryCatch.extractException() + try { + this.free(this.napiExtendedErrorInfoPtr) + this.napiExtendedErrorInfoPtr = NULL + } catch (_) {} + envStore.remove(this.id) } } diff --git a/lib/runtime/index.ts b/lib/runtime/index.ts new file mode 100644 index 00000000..3d64ebf1 --- /dev/null +++ b/lib/runtime/index.ts @@ -0,0 +1,50 @@ +export * from './type' + +export { + preamble, + checkArgs, + checkEnv, + supportFinalizer, + supportBigInt, + canSetFunctionName, + NULL, + envStore, + INT64_RANGE_NEGATIVE, + INT64_RANGE_POSITIVE, + TypedArray, + TryCatch, + EnvStore +} from './util' + +export { + Store, + IStoreValue +} from './Store' + +export { + Handle, + ExternalHandle, + HandleStore +} from './Handle' + +export { + HandleScope, + EscapableHandleScope, + IHandleScope, + ScopeStore +} from './HandleScope' + +export { + Env +} from './env' + +export { + Reference, + RefStore +} from './Reference' + +export { + Deferred, + DeferredStore, + IDeferrdValue +} from './Deferred' diff --git a/lib/runtime/tsconfig.json b/lib/runtime/tsconfig.json index bb021726..ace71520 100644 --- a/lib/runtime/tsconfig.json +++ b/lib/runtime/tsconfig.json @@ -1,11 +1,13 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outFile": "../../dist/library_napi_runtime.js", + "module": "ESNext", "downlevelIteration": true, "declaration": true, "removeComments": true, "types": [], + "declarationDir": "../../dist/runtime", + "emitDeclarationOnly": true, "lib": [ "ES5", "ES2015", @@ -14,16 +16,8 @@ "DOM" ] }, - "files": [ + "include": [ "../typings/napi.d.ts", - "./LinkedList.ts", - "./Store.ts", - "./type.ts", - "./util.ts", - "./Handle.ts", - "./Reference.ts", - "./Deferred.ts", - "./HandleScope.ts", - "./env.ts" + "./**/*.ts" ] } diff --git a/lib/runtime/tsconfig.prod.json b/lib/runtime/tsconfig.prod.json new file mode 100644 index 00000000..b807973a --- /dev/null +++ b/lib/runtime/tsconfig.prod.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "importHelpers": true + } +} diff --git a/lib/runtime/type.ts b/lib/runtime/type.ts index 0859f713..69981f9b 100644 --- a/lib/runtime/type.ts +++ b/lib/runtime/type.ts @@ -1,102 +1,99 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export enum napi_status { - napi_ok, - napi_invalid_arg, - napi_object_expected, - napi_string_expected, - napi_name_expected, - napi_function_expected, - napi_number_expected, - napi_boolean_expected, - napi_array_expected, - napi_generic_failure, - napi_pending_exception, - napi_cancelled, - napi_escape_called_twice, - napi_handle_scope_mismatch, - napi_callback_scope_mismatch, - napi_queue_full, - napi_closing, - napi_bigint_expected, - napi_date_expected, - napi_arraybuffer_expected, - napi_detachable_arraybuffer_expected, - napi_would_deadlock // unused - } +export enum napi_status { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_escape_called_twice, + napi_handle_scope_mismatch, + napi_callback_scope_mismatch, + napi_queue_full, + napi_closing, + napi_bigint_expected, + napi_date_expected, + napi_arraybuffer_expected, + napi_detachable_arraybuffer_expected, + napi_would_deadlock // unused +} - export enum napi_property_attributes { - napi_default = 0, - napi_writable = 1 << 0, - napi_enumerable = 1 << 1, - napi_configurable = 1 << 2, +export enum napi_property_attributes { + napi_default = 0, + napi_writable = 1 << 0, + napi_enumerable = 1 << 1, + napi_configurable = 1 << 2, - // Used with napi_define_class to distinguish static properties - // from instance properties. Ignored by napi_define_properties. - napi_static = 1 << 10, + // Used with napi_define_class to distinguish static properties + // from instance properties. Ignored by napi_define_properties. + napi_static = 1 << 10, - /// #ifdef NAPI_EXPERIMENTAL - // Default for class methods. - napi_default_method = napi_writable | napi_configurable, + /// #ifdef NAPI_EXPERIMENTAL + // Default for class methods. + napi_default_method = napi_writable | napi_configurable, - // Default for object properties, like in JS obj[prop]. - napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable - /// #endif // NAPI_EXPERIMENTAL - } + // Default for object properties, like in JS obj[prop]. + napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable + /// #endif // NAPI_EXPERIMENTAL +} - export enum napi_valuetype { - napi_undefined, - napi_null, - napi_boolean, - napi_number, - napi_string, - napi_symbol, - napi_object, - napi_function, - napi_external, - napi_bigint - } +export enum napi_valuetype { + napi_undefined, + napi_null, + napi_boolean, + napi_number, + napi_string, + napi_symbol, + napi_object, + napi_function, + napi_external, + napi_bigint +} - export enum napi_typedarray_type { - napi_int8_array, - napi_uint8_array, - napi_uint8_clamped_array, - napi_int16_array, - napi_uint16_array, - napi_int32_array, - napi_uint32_array, - napi_float32_array, - napi_float64_array, - napi_bigint64_array, - napi_biguint64_array - } +export enum napi_typedarray_type { + napi_int8_array, + napi_uint8_array, + napi_uint8_clamped_array, + napi_int16_array, + napi_uint16_array, + napi_int32_array, + napi_uint32_array, + napi_float32_array, + napi_float64_array, + napi_bigint64_array, + napi_biguint64_array +} - export enum napi_key_collection_mode { - napi_key_include_prototypes, - napi_key_own_only - } +export enum napi_key_collection_mode { + napi_key_include_prototypes, + napi_key_own_only +} - export enum napi_key_filter { - napi_key_all_properties = 0, - napi_key_writable = 1, - napi_key_enumerable = 1 << 1, - napi_key_configurable = 1 << 2, - napi_key_skip_strings = 1 << 3, - napi_key_skip_symbols = 1 << 4 - } +export enum napi_key_filter { + napi_key_all_properties = 0, + napi_key_writable = 1, + napi_key_enumerable = 1 << 1, + napi_key_configurable = 1 << 2, + napi_key_skip_strings = 1 << 3, + napi_key_skip_symbols = 1 << 4 +} - export enum napi_key_conversion { - napi_key_keep_numbers, - napi_key_numbers_to_strings - } +export enum napi_key_conversion { + napi_key_keep_numbers, + napi_key_numbers_to_strings +} - export enum WrapType { - retrievable, - anonymous - } +export enum WrapType { + retrievable, + anonymous +} - export enum UnwrapAction { - KeepWrap, - RemoveWrap - } +export enum UnwrapAction { + KeepWrap, + RemoveWrap } diff --git a/lib/runtime/util.ts b/lib/runtime/util.ts index c11dc911..f5457651 100644 --- a/lib/runtime/util.ts +++ b/lib/runtime/util.ts @@ -1,121 +1,122 @@ +import { Env } from './env' +import { Store } from './Store' +import { napi_status } from './type' + declare const __webpack_public_path__: any declare const global: typeof globalThis -// eslint-disable-next-line @typescript-eslint/no-unused-vars -namespace emnapi { - export const _global: typeof globalThis = (function () { - let g - g = (function (this: any) { return this })() +export const _global: typeof globalThis = (function () { + let g + g = (function (this: any) { return this })() - try { - // eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval - g = g || new Function('return this')() - } catch (_) { - if (typeof globalThis !== 'undefined') return globalThis - if (typeof __webpack_public_path__ === 'undefined') { - if (typeof global !== 'undefined') return global - } - if (typeof window !== 'undefined') return window - if (typeof self !== 'undefined') return self + try { + // eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval + g = g || new Function('return this')() + } catch (_) { + if (typeof globalThis !== 'undefined') return globalThis + if (typeof __webpack_public_path__ === 'undefined') { + if (typeof global !== 'undefined') return global } + if (typeof window !== 'undefined') return window + if (typeof self !== 'undefined') return self + } - return g - })() + return g +})() - export const NULL: 0 = 0 - export const INT64_RANGE_POSITIVE = Math.pow(2, 63) - export const INT64_RANGE_NEGATIVE = -Math.pow(2, 63) +export const NULL: 0 = 0 +export const INT64_RANGE_POSITIVE = Math.pow(2, 63) +export const INT64_RANGE_NEGATIVE = -Math.pow(2, 63) - export class TryCatch { - private _exception: any = undefined - private _caught: boolean = false - public hasCaught (): boolean { - return this._caught - } +export class TryCatch { + private _exception: any = undefined + private _caught: boolean = false + public hasCaught (): boolean { + return this._caught + } - public exception (): any { - return this._exception - } + public exception (): any { + return this._exception + } - public setError (err: any): void { - this._exception = err - this._caught = true - } + public setError (err: any): void { + this._exception = err + this._caught = true + } - public reset (): void { - this._exception = undefined - this._caught = false - } + public reset (): void { + this._exception = undefined + this._caught = false + } - public extractException (): any { - const e = this._exception - this.reset() - return e - } + public extractException (): any { + const e = this._exception + this.reset() + return e } +} - // eslint-disable-next-line prefer-const - export let nodeVersionPtr: Pointer = NULL +// eslint-disable-next-line prefer-const +export let nodeVersionPtr: Pointer = NULL - class EnvStore extends Store { - public constructor () { - super() - } +export class EnvStore extends Store { + public constructor () { + super() } +} - export const envStore = new EnvStore() +export const envStore = new EnvStore() - /* export function gc (): void { - envStore.forEach(envObject => { - envObject.handleStore.forEach(h => { - h.tryDispose() - }) +/* export function gc (): void { + envStore.forEach(envObject => { + envObject.handleStore.forEach(h => { + h.tryDispose() }) - if (typeof (_global as any).gc === 'function') { - (_global as any).gc() - } - } */ - - export function checkEnv (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { - if ((env === NULL) || !envStore.has(env)) return napi_status.napi_invalid_arg - const envObject = envStore.get(env)! - return fn(envObject) + }) + if (typeof (_global as any).gc === 'function') { + (_global as any).gc() } +} */ - export function checkArgs (envObject: Env, args: any[], fn: () => napi_status): napi_status { - for (let i = 0; i < args.length; i++) { - const arg = args[i] - if (arg === NULL) { - return envObject.setLastError(napi_status.napi_invalid_arg) - } +export function checkEnv (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { + if ((env === NULL) || !envStore.has(env)) return napi_status.napi_invalid_arg + const envObject = envStore.get(env)! + return fn(envObject) +} + +export function checkArgs (envObject: Env, args: any[], fn: () => napi_status): napi_status { + for (let i = 0; i < args.length; i++) { + const arg = args[i] + if (arg === NULL) { + return envObject.setLastError(napi_status.napi_invalid_arg) } - return fn() } + return fn() +} - export function preamble (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { - return checkEnv(env, (envObject) => { - if (envObject.tryCatch.hasCaught()) return envObject.setLastError(napi_status.napi_pending_exception) - envObject.clearLastError() - try { - return fn(envObject) - } catch (err) { - envObject.tryCatch.setError(err) - return envObject.setLastError(napi_status.napi_pending_exception) - } - }) - } +export function preamble (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { + return checkEnv(env, (envObject) => { + if (envObject.tryCatch.hasCaught()) return envObject.setLastError(napi_status.napi_pending_exception) + envObject.clearLastError() + try { + return fn(envObject) + } catch (err) { + envObject.tryCatch.setError(err) + return envObject.setLastError(napi_status.napi_pending_exception) + } + }) +} - export let canSetFunctionName = false - try { - canSetFunctionName = !!Object.getOwnPropertyDescriptor(Function.prototype, 'name')?.configurable - } catch (_) {} +export let canSetFunctionName = false +try { + canSetFunctionName = !!Object.getOwnPropertyDescriptor(Function.prototype, 'name')?.configurable +} catch (_) {} - export const supportFinalizer = (typeof FinalizationRegistry !== 'undefined') && (typeof WeakRef !== 'undefined') - export const supportBigInt = typeof BigInt !== 'undefined' +export const supportFinalizer = (typeof FinalizationRegistry !== 'undefined') && (typeof WeakRef !== 'undefined') +export const supportBigInt = typeof BigInt !== 'undefined' - export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array +export type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array - export function isReferenceType (v: any): v is object { - return (typeof v === 'object' && v !== null) || typeof v === 'function' - } +export function isReferenceType (v: any): v is object { + return (typeof v === 'object' && v !== null) || typeof v === 'function' } diff --git a/lib/typings/napi.d.ts b/lib/typings/napi.d.ts index e8a4e06f..dc491980 100644 --- a/lib/typings/napi.d.ts +++ b/lib/typings/napi.d.ts @@ -17,7 +17,7 @@ declare interface napi_extended_error_info { error_message: const_char_p engine_reserved: void_p engine_error_code: uint32_t; - error_code: emnapi.napi_status; + error_code: number; } declare interface napi_property_descriptor { @@ -29,8 +29,8 @@ declare interface napi_property_descriptor { getter: napi_callback setter: napi_callback value: napi_value - - attributes: emnapi.napi_property_attributes + /* emnapi.napi_property_attributes */ + attributes: number data: void_p } diff --git a/package.json b/package.json index 3533f3c8..5673a79b 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "typings": "index.d.ts", "scripts": { "clean": "cd test && cgen clean", - "build:runtime": "tsc -p ./lib/runtime/tsconfig.json", - "build:lib": "tsc -p ./lib/runtime/tsconfig.json && tsc && node ./script/build.js", - "release": "tsc -p ./lib/runtime/tsconfig.json && tsc && node ./script/build.js&&node ./script/release.js", + "build:runtime": "tsc -p ./lib/runtime/tsconfig.json && node ./script/build-runtime.js", + "build:lib": "npm run build:runtime && tsc && node ./script/build.js", + "release": "npm run build:runtime && tsc && node ./script/build.js&&node ./script/release.js", "prepare": "npm run build:lib", "rebuild": "npm run build:lib && cd test && cgen rebuild -e -d", "rebuild:r": "npm run build:lib && cd test && cgen rebuild -e", @@ -47,8 +47,12 @@ }, "license": "MIT", "devDependencies": { + "@microsoft/api-extractor": "^7.19.4", + "@rollup/plugin-node-resolve": "^13.1.3", + "@rollup/plugin-typescript": "^8.3.0", "@tybys/cgen": "^0.11.1", "@tybys/cross-zip": "^3.1.0", + "@types/fs-extra": "^9.0.13", "@types/node": "^16.11.21", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", @@ -59,9 +63,12 @@ "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.0.0", + "fs-extra": "^10.0.0", "glob": "^7.2.0", "node-addon-api": "4.3.0", - "terser": "^5.10.0", + "rollup": "^2.66.1", + "rollup-plugin-terser": "^7.0.2", + "tslib": "^2.3.1", "typescript": "~4.5.2" } } diff --git a/script/build-runtime.js b/script/build-runtime.js new file mode 100644 index 00000000..4cc9b1a4 --- /dev/null +++ b/script/build-runtime.js @@ -0,0 +1,92 @@ +const path = require('path') +const fs = require('fs-extra') +const rollup = require('rollup') +const rollupTypescript = require('@rollup/plugin-typescript') +const rollupNodeResolve = require('@rollup/plugin-node-resolve').default +const rollupTerser = require('rollup-plugin-terser').terser +const { runtimeOut } = require('./common.js') + +const { + Extractor, + ExtractorConfig +} = require('@microsoft/api-extractor') +const apiExtractorJsonPath = path.join(__dirname, '../lib/runtime/api-extractor.json') +const extractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath) +const extractorResult = Extractor.invoke(extractorConfig, { + localBuild: true, + showVerboseMessages: true +}) +if (extractorResult.succeeded) { + console.log('API Extractor completed successfully') +} else { + console.error(`API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings`) + process.exit(1) +} + +const runtimeDts = extractorConfig.publicTrimmedFilePath + +fs.removeSync(path.join(__dirname, '../dist/runtime')) +fs.appendFileSync(runtimeDts, '\nexport as namespace emnapi;\n', 'utf8') + +const runtimeTsconfigPath = path.join(__dirname, '../lib/runtime/tsconfig.prod.json') + +/** + * @param {boolean=} minify + * @returns {rollup.RollupOptions} + */ +function createInput (minify) { + return { + input: path.join(__dirname, '../lib/runtime/index.ts'), + plugins: [ + rollupNodeResolve({ + mainFields: ['module', 'main'] + }), + rollupTypescript({ + tsconfig: runtimeTsconfigPath + }), + ...(minify + ? [ + rollupTerser({ + compress: true, + mangle: true, + format: { + comments: false + } + }) + ] + : []) + ] + } +} + +Promise.all(([ + { + input: createInput(false), + output: { + file: runtimeOut, + format: 'iife', + name: 'emnapi', + exports: 'named' + } + }, + { + input: createInput(false), + output: { + file: path.join(path.dirname(runtimeOut), 'emnapi.js'), + format: 'umd', + name: 'emnapi', + exports: 'named' + } + }, + { + input: createInput(true), + output: { + file: path.join(path.dirname(runtimeOut), 'emnapi.min.js'), + format: 'umd', + name: 'emnapi', + exports: 'named' + } + } +]).map(conf => { + return rollup.rollup(conf.input).then(bundle => bundle.write(conf.output)) +})) diff --git a/script/build.js b/script/build.js index b5cb486c..b788550f 100644 --- a/script/build.js +++ b/script/build.js @@ -1,11 +1,7 @@ const fs = require('fs') const path = require('path') -const { minify } = require('terser') -const runtimeTsconfigPath = path.join(__dirname, '../lib/runtime/tsconfig.json') -const runtimeTsconfig = JSON.parse(fs.readFileSync(runtimeTsconfigPath, 'utf8')) - -const runtimeOut = path.join(path.dirname(runtimeTsconfigPath), runtimeTsconfig.compilerOptions.outFile) +const { runtimeOut } = require('./common.js') const libTsconfigPath = path.join(__dirname, '../tsconfig.json') const libTsconfig = JSON.parse(fs.readFileSync(libTsconfigPath, 'utf8')) @@ -24,84 +20,41 @@ fs.writeFileSync(libOut, 'utf8' ) -const emnapijs = path.join(path.dirname(runtimeOut), 'emnapi.js') -const emnapijsCode = `(function (root, factory) { - if(typeof exports === 'object' && typeof module === 'object') { - module.exports = factory(); - } else if(typeof define === 'function' && define.amd) { - define([], function () { - return factory(); - }); - } else if(typeof exports === 'object') { - exports['emnapi'] = factory(); - } else { - root['emnapi'] = factory(); - } -})((function (defaultValue) { - var g; - g = (function () { return this; })(); - - try { - g = g || new Function('return this')(); - } catch (_) { - if (typeof globalThis !== 'undefined') return globalThis; - if (typeof __webpack_public_path__ === 'undefined') { - if (typeof global !== 'undefined') return global; - } - if (typeof window !== 'undefined') return window; - if (typeof self !== 'undefined') return self; - } - - return g || defaultValue; -})(this), function () { - ${runtimeCode} - return emnapi; -});` - -fs.writeFileSync(emnapijs, emnapijsCode, 'utf8') - -minify(emnapijsCode, { compress: true, mangle: true, ecma: 5 }).then(res => { - fs.writeFileSync(path.join(path.dirname(runtimeOut), 'emnapi.min.js'), res.code, 'utf8') -}).catch(err => { - console.log(JSON.stringify(err)) -}) - -/* fs.writeFileSync(path.join(path.dirname(runtimeOut), 'emnapi.d.ts'), - `${fs.readFileSync(path.join(path.dirname(runtimeOut), path.basename(runtimeOut, '.js') + '.d.ts'))}\nexport = emnapi;\n`, - 'utf8' -) */ - fs.writeFileSync(path.join(path.dirname(libOut), path.basename(libOut, '.js') + '_no_runtime.js'), libCode .replace('__EMNAPI_RUNTIME_REPLACE__', '""') .replace('__EMNAPI_RUNTIME_INIT__;', ` (function () { - if ('emnapi' in Module) { - emnapi = Module.emnapi + if ('emnapiRuntime' in Module) { + emnapi = Module.emnapiRuntime; } if (!emnapi && typeof require === 'function') { try { - emnapi = require('@tybys/emnapi/dist/emnapi.js') + if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') { + emnapi = require('@tybys/emnapi/dist/emnapi.min.js'); + } else { + emnapi = require('@tybys/emnapi/dist/emnapi.js'); + } } catch (_) {} } if (!emnapi) { emnapi = (function () { - let g - g = (function () { return this })() + let g; + g = (function () { return this })(); try { - g = g || new Function('return this')() + g = g || new Function('return this')(); } catch (_) { - if (typeof globalThis !== 'undefined') return globalThis + if (typeof globalThis !== 'undefined') return globalThis; if (typeof __webpack_public_path__ === 'undefined') { - if (typeof global !== 'undefined') return global + if (typeof global !== 'undefined') return global; } - if (typeof window !== 'undefined') return window - if (typeof self !== 'undefined') return self + if (typeof window !== 'undefined') return window; + if (typeof self !== 'undefined') return self; } - return g - })().emnapi + return g; + })().emnapi; } })();`) .replace(/(makeDynCall\(.*?\))/g, '{{{ $1 }}}') diff --git a/script/common.js b/script/common.js new file mode 100644 index 00000000..170ad88b --- /dev/null +++ b/script/common.js @@ -0,0 +1,5 @@ +const path = require('path') + +const runtimeOut = path.join(__dirname, '../dist/library_napi_runtime.js') + +exports.runtimeOut = runtimeOut diff --git a/test/hello/hello.test.js b/test/hello/hello.test.js index 1a4b5a44..82d4d07c 100644 --- a/test/hello/hello.test.js +++ b/test/hello/hello.test.js @@ -21,7 +21,7 @@ module.exports = new Promise((resolve, reject) => { function load (request) { const mod = require(request) - return typeof mod.default === 'function' ? mod.default({ emnapi: require(${JSON.stringify(join(__dirname, '../../dist/emnapi.js'))}) }).then(({ Module }) => Module.emnapiExports) : Promise.resolve(mod) + return typeof mod.default === 'function' ? mod.default({ emnapiRuntime: require(${JSON.stringify(join(__dirname, '../../dist/emnapi.min.js'))}) }).then(({ Module }) => Module.emnapiExports) : Promise.resolve(mod) } load(${JSON.stringify(getEntry('hello'))}).then((binding) => { const msg = binding.hello(); parentPort.postMessage(msg) });`, { eval: true, env: process.env }) .on('message', common.mustCall((msg) => { diff --git a/test/util.js b/test/util.js index fb6c0cb5..1b4c2252 100644 --- a/test/util.js +++ b/test/util.js @@ -15,7 +15,7 @@ exports.load = function (targetName) { if (typeof mod.default === 'function') { const p = new Promise((resolve, reject) => { mod.default({ - emnapi: require('../dist/emnapi.js'), + emnapiRuntime: require('../dist/emnapi.min.js'), onEmnapiInitialized: (err) => { if (err) { reject(err)