Skip to content

Commit

Permalink
Merge pull request #43 from toyobayashi/feat-symbol-ref
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi committed Mar 29, 2023
2 parents f6da36b + 88bfb98 commit b973e45
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 69 deletions.
2 changes: 2 additions & 0 deletions packages/emnapi/src/core/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ function emnapiUtf8ToString (ptr: number, length: number): string {
return UTF8ToString(ptr)
}
length = length >>> 0
if (!length) return ''
const HEAPU8 = new Uint8Array(wasmMemory.buffer)
const shared = (typeof SharedArrayBuffer === 'function') && (wasmMemory.buffer instanceof SharedArrayBuffer)
return emnapiUtf8Decoder.decode(shared ? HEAPU8.slice(ptr, ptr + length) : HEAPU8.subarray(ptr, ptr + length))
Expand Down Expand Up @@ -225,6 +226,7 @@ function emnapiUtf16ToString (ptr: number, length: number): string {
return UTF16ToString(ptr)
}
length = length >>> 0
if (!length) return ''
const HEAPU8 = new Uint8Array(wasmMemory.buffer)
const shared = (typeof SharedArrayBuffer === 'function') && (wasmMemory.buffer instanceof SharedArrayBuffer)
return emnapiUtf16leDecoder.decode(shared ? HEAPU8.slice(ptr, ptr + length * 2) : HEAPU8.subarray(ptr, ptr + length * 2))
Expand Down
2 changes: 2 additions & 0 deletions packages/emnapi/src/emscripten/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ mergeInto(LibraryManager.library, {
return UTF8ToString(ptr)
}
length = length >>> 0
if (!length) return ''
return emnapiUtf8Decoder.decode($getUnsharedTextDecoderView('HEAPU8', 'ptr', 'ptr + length'))
},

Expand Down Expand Up @@ -135,6 +136,7 @@ mergeInto(LibraryManager.library, {
return UTF16ToString(ptr)
}
length = length >>> 0
if (!length) return ''
return emnapiUtf16leDecoder.decode($getUnsharedTextDecoderView('HEAPU8', 'ptr', 'ptr + length * 2'))
}
})
4 changes: 2 additions & 2 deletions packages/emnapi/src/life.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ function napi_create_reference (
$CHECK_ARG!(envObject, result)

const handle = emnapiCtx.handleStore.get(value)!
if (!(handle.isObject() || handle.isFunction())) {
return envObject.setLastError(napi_status.napi_object_expected)
if (!(handle.isObject() || handle.isFunction() || handle.isSymbol())) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ref = emnapiCtx.createReference(envObject, handle.id, initial_refcount >>> 0, Ownership.kUserland as any)
Expand Down
30 changes: 22 additions & 8 deletions packages/emnapi/src/value/convert2napi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,21 @@ function napi_create_double (env: napi_env, value: double, result: Pointer<napi_
function napi_create_string_latin1 (env: napi_env, str: const_char_p, length: size_t, result: Pointer<napi_value>): napi_status {
$CHECK_ENV!(env)
const envObject = emnapiCtx.envStore.get(env)!
$CHECK_ARG!(envObject, result)
$from64('str')
$from64('length')
const autoLength = length === -1
length = length >>> 0
if (!((length === 0xffffffff) || (length <= 2147483647)) || (!str)) {
if (length !== 0) {
$CHECK_ARG!(envObject, str)
}
$CHECK_ARG!(envObject, result)
$from64('str')
if (!(autoLength || (length <= 2147483647))) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}

let latin1String = ''
let len = 0
if (length === -1) {
if (autoLength) {
while (true) {
const ch = $makeGetValue('str', 0, 'u8') as number
if (!ch) break
Expand All @@ -96,11 +100,16 @@ function napi_create_string_latin1 (env: napi_env, str: const_char_p, length: si
function napi_create_string_utf16 (env: napi_env, str: const_char16_t_p, length: size_t, result: Pointer<napi_value>): napi_status {
$CHECK_ENV!(env)
const envObject = emnapiCtx.envStore.get(env)!
$from64('length')
const autoLength = length === -1
const sizelength = length >>> 0
if (length !== 0) {
$CHECK_ARG!(envObject, str)
}
$CHECK_ARG!(envObject, result)
$from64('str')
$from64('length')

if (((length < -1) || (length > 2147483647)) || (!str)) {
if (!(autoLength || (sizelength <= 2147483647))) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}

Expand All @@ -115,11 +124,16 @@ function napi_create_string_utf16 (env: napi_env, str: const_char16_t_p, length:
function napi_create_string_utf8 (env: napi_env, str: const_char_p, length: size_t, result: Pointer<napi_value>): napi_status {
$CHECK_ENV!(env)
const envObject = emnapiCtx.envStore.get(env)!
$from64('length')
const autoLength = length === -1
const sizelength = length >>> 0
if (length !== 0) {
$CHECK_ARG!(envObject, str)
}
$CHECK_ARG!(envObject, result)
$from64('str')
$from64('length')

if (((length < -1) || (length > 2147483647)) || (!str)) {
if (!(autoLength || (sizelength <= 2147483647))) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}
const utf8String = emnapiUtf8ToString(str, length)
Expand Down
8 changes: 7 additions & 1 deletion packages/emnapi/src/value/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,13 @@ function node_api_symbol_for (env: napi_env, utf8description: const_char_p, leng
$from64('utf8description')
$from64('result')

if (((length < -1) || (length > 2147483647)) || (!utf8description)) {
const autoLength = length === -1
const sizelength = length >>> 0
if (length !== 0) {
$CHECK_ARG!(envObject, utf8description)
}

if (!(autoLength || (sizelength <= 2147483647))) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}

Expand Down
2 changes: 2 additions & 0 deletions packages/runtime/src/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
_global,
supportReflect,
supportFinalizer,
supportWeakSymbol,
supportBigInt,
supportNewFunction,
canSetFunctionName,
Expand Down Expand Up @@ -115,6 +116,7 @@ export class Context {
public feature = {
supportReflect,
supportFinalizer,
supportWeakSymbol,
supportBigInt,
supportNewFunction,
canSetFunctionName,
Expand Down
80 changes: 49 additions & 31 deletions packages/runtime/src/Persistent.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import { supportFinalizer } from './util'

export class Persistent<T extends object> {
private _ref: T | WeakRef<T> | null
class StrongRef<T> {
private _value: T

constructor (value: T) {
this._value = value
}

deref (): T {
return this._value
}

dispose (): void {
this._value = undefined!
}
}

export class Persistent<T> {
private _ref: StrongRef<T> | WeakRef<T extends object ? T : never> | undefined
private _param: any
private _callback: ((param: any) => void) | undefined

private static readonly _registry = supportFinalizer
? new FinalizationRegistry((value: Persistent<any>) => {
value._ref = null
value._ref = undefined
const callback = value._callback
const param = value._param
value._callback = undefined
Expand All @@ -19,62 +35,64 @@ export class Persistent<T extends object> {
: undefined!

constructor (value: T) {
this._ref = value
this._ref = new StrongRef(value)
}

setWeak<P> (param: P, callback: (param: P) => void): void {
if (this._ref === null) return
if (!supportFinalizer) return
if (this._ref instanceof WeakRef) return
this._param = param
this._callback = callback
Persistent._registry.register(this._ref, this, this)
this._ref = new WeakRef<T>(this._ref)
if (!supportFinalizer || this._ref === undefined || this._ref instanceof WeakRef) return
const value = this._ref.deref()
try {
// try {
Persistent._registry.register(value as any, this, this)
const weakRef = new WeakRef<any>(value)
this._ref.dispose()
this._ref = weakRef
// } catch (_) {
// Persistent._registry.register(this._ref, this, this)
// this._ref = new WeakRef<any>(this._ref)
// }
this._param = param
this._callback = callback
} catch (_) {}
}

clearWeak (): void {
if (this._ref === null) return
if (!supportFinalizer) return
if (!supportFinalizer || this._ref === undefined) return
if (this._ref instanceof WeakRef) {
try {
Persistent._registry.unregister(this)
} catch (_) {}
this._param = undefined
this._callback = undefined
this._ref = this._ref.deref() as T
const value = this._ref.deref()
if (value === undefined) {
this._ref = value
} else {
this._ref = new StrongRef(value as T)
}
}
}

reset (other?: T | WeakRef<T>): void {
reset (): void {
if (supportFinalizer) {
try {
Persistent._registry.unregister(this)
} catch (_) {}
}
this._param = undefined
this._callback = undefined
if (other) {
this._ref = other
} else {
this._ref = null
if (this._ref instanceof StrongRef) {
this._ref.dispose()
}
this._ref = undefined
}

isEmpty (): boolean {
return this._ref === null
return this._ref === undefined
}

deref (): T | undefined {
if (!supportFinalizer) {
return (this._ref as T | null) ?? undefined
}

if (this._ref === null) return undefined

if (this._ref instanceof WeakRef) {
return this._ref.deref()
}

return this._ref
if (this._ref === undefined) return undefined
return this._ref.deref()
}
}
6 changes: 1 addition & 5 deletions packages/runtime/src/Reference.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { IStoreValue } from './Store'
import { isReferenceType } from './util'
import type { Env } from './env'
import { RefBase } from './RefBase'
import { Persistent } from './Persistent'
Expand All @@ -22,12 +21,9 @@ export class Reference extends RefBase implements IStoreValue {
finalize_hint: void_p = 0
): Reference {
const handle = envObject.ctx.handleStore.get(handle_id)!
if (!isReferenceType(handle.value)) {
throw new TypeError('Invalid reference value')
}
const ref = new Reference(envObject, initialRefcount, ownership, finalize_callback, finalize_data, finalize_hint)
envObject.ctx.refStore.add(ref)
ref.persistent = new Persistent<object>(handle.value)
ref.persistent = new Persistent(handle.value)

if (initialRefcount === 0) {
ref._setWeak()
Expand Down
12 changes: 12 additions & 0 deletions packages/runtime/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ export const canSetFunctionName = /*#__PURE__*/ (function () {

export const supportReflect = typeof Reflect === 'object'
export const supportFinalizer = (typeof FinalizationRegistry !== 'undefined') && (typeof WeakRef !== 'undefined')
export const supportWeakSymbol = /*#__PURE__*/ (function () {
try {
// eslint-disable-next-line symbol-description
const sym = Symbol()
// eslint-disable-next-line no-new
new WeakRef(sym as any)
new WeakMap().set(sym as any, undefined)
} catch (_) {
return false
}
return true
})()
export const supportBigInt = typeof BigInt !== 'undefined'

export function isReferenceType (v: any): v is object {
Expand Down
2 changes: 2 additions & 0 deletions packages/test/async/async.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ async function main () {
throw new Error('uncaught')
}))
})

process.exitCode = 0
}

module.exports = main()
Expand Down
42 changes: 20 additions & 22 deletions packages/test/ref/ref.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,29 @@ module.exports = p.then(test_reference => {
// Run each test function in sequence,
// with an async delay and GC call between each.
async function runTests () {
// emnapi can not create reference on a symbol
// https://github.com/tc39/proposal-symbols-as-weakmap-keys
(() => {
const symbol = test_reference.createSymbol('testSym')
test_reference.createReference(symbol, 0)
assert.strictEqual(test_reference.referenceValue, symbol)
})()
test_reference.deleteReference();

// (() => {
// const symbol = test_reference.createSymbol('testSym')
// test_reference.createReference(symbol, 0)
// assert.strictEqual(test_reference.referenceValue, symbol)
// })()
// test_reference.deleteReference();

// (() => {
// const symbol = test_reference.createSymbolFor('testSymFor')
// test_reference.createReference(symbol, 0)
// assert.strictEqual(test_reference.referenceValue, symbol)
// assert.strictEqual(test_reference.referenceValue, Symbol.for('testSymFor'))
// })()
// test_reference.deleteReference();
(() => {
const symbol = test_reference.createSymbolFor('testSymFor')
test_reference.createReference(symbol, 0)
assert.strictEqual(test_reference.referenceValue, symbol)
assert.strictEqual(test_reference.referenceValue, Symbol.for('testSymFor'))
})()
test_reference.deleteReference();

// (() => {
// const symbol = test_reference.createSymbolForEmptyString()
// test_reference.createReference(symbol, 0)
// assert.strictEqual(test_reference.referenceValue, symbol)
// assert.strictEqual(test_reference.referenceValue, Symbol.for(''))
// })()
// test_reference.deleteReference()
(() => {
const symbol = test_reference.createSymbolForEmptyString()
test_reference.createReference(symbol, 0)
assert.strictEqual(test_reference.referenceValue, symbol)
assert.strictEqual(test_reference.referenceValue, Symbol.for(''))
})()
test_reference.deleteReference()

assert.throws(() => test_reference.createSymbolForIncorrectLength(),
/Invalid argument/);
Expand Down

0 comments on commit b973e45

Please sign in to comment.