Skip to content

Commit

Permalink
refactor: store external value in separated WeakMap (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi authored Jan 22, 2025
1 parent 7fca935 commit a341c1c
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 24 deletions.
2 changes: 1 addition & 1 deletion packages/emnapi/src/value/convert2c.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ export function napi_get_value_external (env: napi_env, value: napi_value, resul
from64('result')

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const p = handle.data(envObject)
const p = handle.data()
makeSetValue('result', 0, 'p', '*')
return envObject.clearLastError()
}
Expand Down
2 changes: 1 addition & 1 deletion packages/emnapi/src/value/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function napi_create_external (env: napi_env, data: void_p, finalize_cb:
if (!emnapiCtx.feature.supportFinalizer && finalize_cb) {
throw emnapiCtx.createNotSupportWeakRefError('napi_create_external', 'Parameter "finalize_cb" must be 0(NULL)')
}
const externalHandle = emnapiCtx.getCurrentScope()!.addExternal(envObject, data)
const externalHandle = emnapiCtx.getCurrentScope()!.addExternal(data)
if (finalize_cb) {
emnapiCtx.createReferenceWithFinalizer(envObject, externalHandle.id, 0, ReferenceOwnership.kRuntime as any, finalize_cb, data, finalize_hint)
}
Expand Down
30 changes: 30 additions & 0 deletions packages/runtime/src/External.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @public */
export interface External extends Record<any, any> {}

const externalValue = new WeakMap<External, number | bigint>()

/** @public */
export function isExternal (object: unknown): object is External {
return externalValue.has(object as any)
}

/** @public */ // eslint-disable-next-line @typescript-eslint/no-redeclare
export const External = (() => {
function External (this: External, value: number | bigint): void {
Object.setPrototypeOf(this, null)
externalValue.set(this, value)
}
External.prototype = null as any
return External as unknown as {
new (value: number | bigint): External
prototype: null
}
})()

/** @public */
export function getExternalValue (external: External): number | bigint {
if (!isExternal(external)) {
throw new TypeError('not external')
}
return externalValue.get(external)!
}
15 changes: 5 additions & 10 deletions packages/runtime/src/Handle.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { Env } from './env'
import { isReferenceType, _global, _Buffer } from './util'
import { External, getExternalValue, isExternal } from './External'
import { _global, _Buffer } from './util'

export class Handle<S> {
public constructor (
public id: number,
public value: S
) {}

public data (envObject: Env): void_p {
return envObject.getObjectBinding(this.value as any).data
public data (): void_p {
return getExternalValue(this.value as External) as void_p
}

public isNumber (): boolean {
Expand All @@ -28,7 +28,7 @@ export class Handle<S> {
}

public isExternal (): boolean {
return (isReferenceType(this.value) && Object.getPrototypeOf(this.value) === null)
return isExternal(this.value)
}

public isObject (): boolean {
Expand Down Expand Up @@ -94,11 +94,6 @@ export class ConstHandle<S extends undefined | null | boolean | typeof globalThi
public override dispose (): void {}
}

export function External (this: any): void {
Object.setPrototypeOf(this, null)
}
External.prototype = null as any

export class HandleStore {
public static UNDEFINED = new ConstHandle(GlobalHandle.UNDEFINED, undefined)
public static NULL = new ConstHandle(GlobalHandle.NULL, null)
Expand Down
12 changes: 3 additions & 9 deletions packages/runtime/src/HandleScope.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Env } from './env'
import type { Handle, HandleStore } from './Handle'
import { External } from './Handle'
import { External } from './External'

export class HandleScope {
public handleStore: HandleStore
Expand Down Expand Up @@ -28,13 +27,8 @@ export class HandleScope {
return h
}

public addExternal (envObject: Env, data: void_p): Handle<object> {
const value = new (External as any)()
const h = envObject.ctx.handleStore.push(value)
const binding = envObject.initObjectBinding(value)
binding.data = data
this.end++
return h
public addExternal (data: void_p): Handle<object> {
return this.add(new External(data))
}

public dispose (): void {
Expand Down
4 changes: 1 addition & 3 deletions packages/runtime/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function handleThrow (envObject: Env, value: any): void {
export interface IReferenceBinding {
wrapped: number // wrapped Reference id
tag: Uint32Array | null
data: void_p
}

export abstract class Env implements IStoreValue {
Expand Down Expand Up @@ -198,8 +197,7 @@ export abstract class Env implements IStoreValue {
public initObjectBinding<S extends object> (value: S): IReferenceBinding {
const binding: IReferenceBinding = {
wrapped: 0,
tag: null,
data: 0
tag: null
}
this._bindingMap.set(value, binding)
return binding
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { createContext, getDefaultContext, Context, type CleanupHookCallbackFunc
export { Deferred, type IDeferrdValue } from './Deferred'
export { Env, NodeEnv, type IReferenceBinding } from './env'
export { EmnapiError, NotSupportWeakRefError, NotSupportBufferError } from './errors'
export { External, isExternal, getExternalValue } from './External'
export { Finalizer } from './Finalizer'
export { TrackedFinalizer } from './TrackedFinalizer'
export { Handle, ConstHandle, HandleStore } from './Handle'
Expand Down

0 comments on commit a341c1c

Please sign in to comment.