-
-
Save devsnek/db5499bf774f078e9ebb679680bd2cd1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const util = require('util'); | |
const NAPI_OK = 0; | |
const NAPI_INVALID_ARG = 1; | |
const NAPI_OBJECT_EXPECTED = 2; | |
const NAPI_STRING_EXPECTED = 3; | |
const NAPI_NAME_EXPECTED = 4; | |
const NAPI_FUNCTION_EXPECTED = 5; | |
const NAPI_NUMBER_EXPECTED = 6; | |
const NAPI_BOOLEAN_EXPECTED = 7; | |
const NAPI_ARRAY_EXPECTED = 8; | |
const NAPI_GENERIC_FAILURE = 9; | |
const NAPI_PENDING_EXCEPTION = 10; | |
const NAPI_CANCELLED = 11; | |
const NAPI_ESCAPE_CALLED_TWICE = 12; | |
const NAPI_HANDLE_SCOPE_MISMATCH = 13; | |
const NAPI_CALLBACK_SCOPE_MISMATCH = 14; | |
const NAPI_QUEUE_FULL = 15; | |
const NAPI_CLOSING = 16; | |
const NAPI_BIGINT_EXPECTED = 17; | |
const NAPI_DATE_EXPECTED = 18; | |
const NAPI_UNDEFINED = 0; | |
const NAPI_NULL = 1; | |
const NAPI_BOOLEAN = 2; | |
const NAPI_NUMBER = 3; | |
const NAPI_STRING = 4; | |
const NAPI_SYMBOL = 5; | |
const NAPI_OBJECT = 6; | |
const NAPI_FUNCTION = 7; | |
const NAPI_EXTERNAL = 8; | |
const NAPI_BIGINT = 9; | |
const NAPI_DEFAULT = 0; | |
const NAPI_WRITABLE = 1 << 0; | |
const NAPI_ENUMERATE = 1 << 1; | |
const NAPI_CONFIGURABLE = 1 << 2; | |
const NAPI_STATIC = 1 << 10; | |
const kFuncNormal = Symbol('kFuncNormal'); | |
const kFuncConstructor = Symbol('kFuncConstructor'); | |
const kFuncMethod = Symbol('kFuncMethod'); | |
class NAPI { | |
indirectFunctionTable = undefined; | |
memory = undefined; | |
view = undefined; | |
handleScope = []; | |
references = []; | |
externalData = new WeakMap(); | |
wrapData = new WeakMap(); | |
finalizationGroup = new FinalizationGroup((items) => { | |
for (const [cbIdx, envPtr, data, hint] of items) { | |
try { | |
this.indirectFunctionTable.get(cbIdx)(envPtr, data, hint); | |
} catch (e) { | |
internalBinding('task_queue').triggerFatalException(e); | |
} | |
} | |
}); | |
exception = null; | |
exceptionPending = false; | |
wrap(f, bypass = false) { | |
return (...args) => { | |
if (!bypass && this.exceptionPending) { | |
return NAPI_PENDING_EXCEPTION; | |
} | |
try { | |
this.refreshMemory(); | |
return f(...args); | |
} catch (e) { | |
this.exception = e; | |
this.exceptionPending = true; | |
return NAPI_PENDING_EXCEPTION; | |
} | |
}; | |
} | |
createFunction(envPtr, cb, data, name, mode = kFuncNormal) { | |
const self = this; | |
const func = (0, function (...args) { // eslint-disable-line func-names | |
if (mode === kFuncConstructor && new.target === undefined) { | |
throw new TypeError(`Class constructor ${name ? `${name} ` : ''}cannot be invoked without 'new'`); | |
} | |
if (mode === kFuncMethod && new.target !== undefined) { | |
throw new TypeError(`${name ? `${name} is n` : 'N'}ot a constructor`); | |
} | |
self.openHandleScope(); | |
try { | |
const callbackInfo = { | |
this: this, | |
newTarget: new.target, | |
args, | |
data, | |
}; | |
const idx = self.indirectFunctionTable.get(cb)(envPtr, self.store(callbackInfo)); | |
return self.load(idx); | |
} finally { | |
self.closeHandleScope(); | |
if (self.exceptionPending) { | |
self.exceptionPending = false; | |
throw self.exception; // eslint-disable-line no-unsafe-finally | |
} | |
} | |
}); | |
if (name) { | |
Object.defineProperty(func, 'name', { | |
value: name, | |
configurable: true, | |
}); | |
} | |
return func; | |
} | |
readPropertyDescriptors(count, ptr) { | |
const descriptors = []; | |
for (let i = 0; i < count; i += 1) { | |
const utf8namePtr = this.readUint32(ptr); | |
ptr += 4; | |
const nameIdx = this.readUint32(ptr); | |
ptr += 4; | |
const method = this.readUint32(ptr); | |
ptr += 4; | |
const getter = this.readUint32(ptr); | |
ptr += 4; | |
const setter = this.readUint32(ptr); | |
ptr += 4; | |
const valueIdx = this.readUint32(ptr); | |
ptr += 4; | |
const attributes = this.readUint32(ptr); | |
ptr += 4; | |
const dataPtr = this.readUint32(ptr); | |
ptr += 4; | |
const name = utf8namePtr | |
? this.readCStringFrom(utf8namePtr) | |
: this.load(nameIdx); | |
if (typeof name !== 'string' || typeof name !== 'symbol') { | |
return NAPI_NAME_EXPECTED; | |
} | |
const descriptor = {}; | |
if ((attributes & NAPI_WRITABLE) === 0) { | |
descriptor.writable = false; | |
} | |
if ((attributes & NAPI_ENUMERATE) === 0) { | |
descriptor.enumerable = false; | |
} | |
if ((attributes & NAPI_CONFIGURABLE) === 0) { | |
descriptor.configurable = false; | |
} | |
if ((attributes & NAPI_STATIC) === NAPI_STATIC) { | |
descriptor.static = true; | |
} | |
descriptors.push({ | |
method, | |
getter, | |
setter, | |
valueIdx, | |
dataPtr, | |
descriptor, | |
name, | |
}); | |
} | |
return descriptors; | |
} | |
exports = { | |
napi_get_last_error_info: () => {}, // PENDING | |
napi_throw: this.wrap((envPtr, valueIdx) => { | |
this.exception = this.load(valueIdx); | |
this.exceptionPending = true; | |
return NAPI_OK; | |
}), | |
napi_throw_error: this.wrap((envPtr, codePtr, msgPtr) => {}), | |
napi_throw_type_error: this.wrap((envPtr, codePtr, msgPtr) => {}), | |
napi_throw_range_error: this.wrap((envPtr, codePtr, msgPtr) => {}), | |
napi_create_error: this.wrap((envPtr, codeIdx, msgIdx, resultPtr) => {}), | |
napi_create_type_error: this.wrap((envPtr, codeIdx, msgIdx, resultPtr) => {}), | |
napi_create_range_error: this.wrap((envPtr, codeIdx, msgIdx, resultPtr) => {}), | |
napi_get_and_clear_last_exception: (envPtr, resultPtr) => { | |
const e = this.exception; | |
this.exception = null; | |
this.exceptionPending = false; | |
this.refreshMemory(); | |
this.writeValue(resultPtr, this.store(e)); | |
return NAPI_OK; | |
}, | |
napi_is_exception_pending: (envPtr, resultPtr) => { | |
this.refreshMemory(); | |
this.view.setUint8(resultPtr, this.exceptionPending); | |
return NAPI_OK; | |
}, | |
napi_fatal_exception: this.wrap((envPtr, valueIdx) => { | |
const value = this.load(valueIdx); | |
internalBinding('task_queue').triggerFatalException(value); | |
return NAPI_OK; | |
}), | |
napi_fatal_error: (locationPtr, locationLen, messagePtr, messageLen) => { | |
this.refreshMemory(); | |
const location = Buffer.from(this.memory.buffer, locationPtr, locationLen).toString('utf8'); | |
const message = Buffer.from(this.memory.buffer, messagePtr, messageLen).toString('utf8'); | |
if (location) { | |
process._rawDebug(`FATAL ERROR: ${location} ${message}`); | |
} else { | |
process._rawDebug(`FATAL ERROR: ${message}`); | |
} | |
process.abort(); | |
// unreachable | |
return NAPI_GENERIC_FAILURE; | |
}, | |
napi_open_handle_scope: this.wrap((envPtr, resultPtr) => { | |
this.openHandleScope(); | |
this.view.setUint32(resultPtr, this.handleScope.length - 1, true); | |
return NAPI_OK; | |
}), | |
napi_close_handle_scope: (envPtr, scopePtr) => { | |
if (scopePtr !== this.handleScope.length - 1) { | |
return NAPI_HANDLE_SCOPE_MISMATCH; | |
} | |
this.closeHandleScope(); | |
return NAPI_OK; | |
}, | |
napi_open_escapable_handle_scope: this.wrap((envPtr, resultPtr) => { | |
this.openHandleScope(); | |
this.view.setUint32(resultPtr, this.scope.length - 1, true); | |
return NAPI_OK; | |
}), | |
napi_close_escapable_handle_scope: (envPtr, scopePtr) => { | |
if (scopePtr !== this.handleScope.length - 1) { | |
return NAPI_HANDLE_SCOPE_MISMATCH; | |
} | |
this.closeHandleScope(); | |
return NAPI_OK; | |
}, | |
napi_escape_handle: (envPtr, scopePtr, escapeeIdx, resultPtr) => {}, // PENDING | |
napi_create_reference: this.wrap((envPtr, valueIdx, initialRefcount, resultPtr) => { | |
const value = this.load(valueIdx); | |
const ref = { value, ref: initialRefcount }; | |
this.references.push(ref); | |
this.writeValue(resultPtr, this.references.length - 1); | |
return NAPI_OK; | |
}), | |
napi_delete_reference: (envPtr, refIdx) => { | |
this.references[refIdx] = undefined; | |
return NAPI_OK; | |
}, | |
napi_reference_ref: this.wrap((envPtr, refIdx, resultPtr) => { | |
const ref = this.references[refIdx]; | |
if (ref.ref === 0) { | |
return NAPI_GENERIC_FAILURE; | |
} | |
ref.ref += 1; | |
if (resultPtr) { | |
this.view.setUint32(resultPtr, ref.ref, true); | |
} | |
return NAPI_OK; | |
}), | |
napi_reference_unref: this.wrap((envPtr, refIdx, resultPtr) => { | |
const ref = this.references[refIdx]; | |
if (ref.ref === 0) { | |
return NAPI_GENERIC_FAILURE; | |
} | |
ref.ref -= 1; | |
if (resultPtr) { | |
this.view.setUint32(resultPtr, ref.ref, true); | |
} | |
return NAPI_OK; | |
}), | |
napi_get_reference_value: this.wrap((envPtr, refIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(this.references[refIdx].value)); | |
return NAPI_OK; | |
}), | |
// napi_add_env_cleanup_hook: () => {}, | |
// napi_remove_env_cleanup_hook: () => {}, | |
napi_create_array: this.wrap((envPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store([])); | |
return NAPI_OK; | |
}), | |
napi_create_array_with_length: this.wrap((envPtr, length, resultPtr) => { | |
this.writeValue(resultPtr, this.store(new Array(length))); | |
return NAPI_OK; | |
}), | |
napi_create_arraybuffer: this.wrap((envPtr, length, dataPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store(this.memory.buffer.slice(dataPtr, dataPtr + length))); | |
return NAPI_OK; | |
}), | |
napi_create_date: this.wrap((envPtr, time, resultPtr) => { | |
this.writeValue(resultPtr, this.store(new Date(time))); | |
return NAPI_OK; | |
}), | |
napi_create_external: this.wrap((envPtr, data, resultPtr) => { | |
const obj = {}; | |
this.externalData.set(obj, data); | |
this.writeValue(resultPtr, this.store(obj)); | |
return NAPI_OK; | |
}), | |
napi_create_object: this.wrap((envPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store({})); | |
return NAPI_OK; | |
}), | |
napi_create_symbol: this.wrap((envPtr, descriptionIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(Symbol(this.load(descriptionIdx)))); | |
return NAPI_OK; | |
}), | |
napi_create_typedarray: this.wrap((envPtr, type, length, arraybufferIdx, | |
byteOffset, resultPtr) => { | |
const ab = new [ | |
Int8Array, | |
Uint8Array, | |
Uint8ClampedArray, | |
Int16Array, | |
Uint16Array, | |
Int32Array, | |
Uint32Array, | |
Float32Array, | |
Float64Array, | |
BigInt64Array, | |
BigUint64Array, | |
][type](this.load(arraybufferIdx), byteOffset, length); | |
this.writeValue(resultPtr, this.store(ab)); | |
return NAPI_OK; | |
}), | |
napi_create_dataview: this.wrap((envPtr, byteLength, arraybufferIdx, byteOffset, resultPtr) => { | |
const dv = new DataView(this.load(arraybufferIdx), byteOffset, byteLength); | |
this.writeValue(resultPtr, this.store(dv)); | |
return NAPI_OK; | |
}), | |
napi_create_int32: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_uint32: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_int64: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_double: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_bigint_int64: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_bigint_uint64: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_bigint_words: this.wrap((envPtr, signBit, wordCount, wordsPtr, resultPtr) => { | |
let b = 0n; | |
let shift = 0n; | |
for (let i = 0; i < wordCount; i += 1) { | |
const word = this.view.getBigUint64((i * 64) + wordsPtr, true); | |
b += word << shift; | |
shift += 64n; | |
} | |
const value = ((-1) ** signBit) * b; | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_create_string_latin1: this.wrap((envPtr, strPtr, length, resultPtr) => { | |
const str = Buffer.from(this.memory.buffer, strPtr, length).toString('latin1'); | |
this.writeValue(resultPtr, this.store(str)); | |
return NAPI_OK; | |
}), | |
napi_create_string_utf16: this.wrap((envPtr, strPtr, length, resultPtr) => { | |
const str = Buffer.from(this.memory.buffer, strPtr, length).toString('utf16'); | |
this.writeValue(resultPtr, this.store(str)); | |
return NAPI_OK; | |
}), | |
napi_create_string_utf8: this.wrap((envPtr, strPtr, length, resultPtr) => { | |
const str = Buffer.from(this.memory.buffer, strPtr, length).toString('utf8'); | |
this.writeValue(resultPtr, this.store(str)); | |
return NAPI_OK; | |
}), | |
napi_get_array_length: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const a = this.load(valueIdx); | |
if (!util.types.isArray(a)) { | |
return NAPI_ARRAY_EXPECTED; | |
} | |
this.view.setUint32(resultPtr, a.length, true); | |
return NAPI_OK; | |
}), | |
napi_get_prototype: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const proto = Object.getPrototypeOf(this.load(valueIdx)); | |
this.writeValue(resultPtr, this.store(proto)); | |
return NAPI_OK; | |
}), | |
get_date_value: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const d = this.load(valueIdx); | |
if (!util.types.isDate(d)) { | |
return NAPI_DATE_EXPECTED; | |
} | |
this.view.setFloat64(resultPtr, d.getTime(), true); | |
return NAPI_OK; | |
}), | |
get_value_bool: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const b = this.load(valueIdx); | |
if (typeof b !== 'boolean') { | |
return NAPI_BOOLEAN_EXPECTED; | |
} | |
this.view.setUint8(resultPtr, b); | |
return NAPI_OK; | |
}), | |
napi_get_value_double: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const v = this.load(valueIdx); | |
if (typeof v !== 'number') { | |
return NAPI_NUMBER_EXPECTED; | |
} | |
this.view.setFloat64(resultPtr, v, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_bigint_int64: this.wrap((envPtr, valueIdx, resultPtr, losslessPtr) => { | |
const b = this.load(valueIdx); | |
if (typeof b !== 'bigint') { // eslint-disable-line valid-typeof | |
return NAPI_BIGINT_EXPECTED; | |
} | |
if (losslessPtr !== 0) { | |
this.view.setUint8(losslessPtr, BigInt.asIntN(64, b) !== b); | |
} | |
this.view.setBigInt64(resultPtr, b, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_bigint_uint64: this.wrap((envPtr, valueIdx, resultPtr, losslessPtr) => { | |
const b = this.load(valueIdx); | |
if (typeof b !== 'bigint') { // eslint-disable-line valid-typeof | |
return NAPI_BIGINT_EXPECTED; | |
} | |
if (losslessPtr !== 0) { | |
this.view.setUint8(losslessPtr, BigInt.asUintN(64, b) !== b); | |
} | |
this.view.setBigUint64(resultPtr, b, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_bigint_words: this.wrap((envPtr, valueIdx, signBitPtr, | |
wordCountPtr, wordsPtr) => { | |
const b = this.load(valueIdx); | |
if (typeof b !== 'bigint') { // eslint-disable-line valid-typeof | |
return NAPI_BIGINT_EXPECTED; | |
} | |
if (signBitPtr !== 0) { | |
this.view.setUint8(signBitPtr, b < 0n); | |
} | |
const wordCount = this.view.getUint32(signBitPtr, true); | |
let i = 0; | |
let ull = b < 0n ? -b : b; | |
for (; i < wordCount; i += 1) { | |
const bv = ull & ((1n << 64n) - 1); | |
this.view.setBigUint64(wordsPtr + (i * 64), bv, true); | |
ull >>= 64n; | |
} | |
while (ull > 0) { | |
i += 1; | |
ull >>= 64n; | |
} | |
this.view.setUint32(wordCountPtr, i, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_external: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const e = this.load(valueIdx); | |
if (!this.externalData.has(e)) { | |
return NAPI_INVALID_ARG; | |
} | |
this.view.setUint32(resultPtr, this.externalData.get(e), true); | |
return NAPI_OK; | |
}), | |
napi_get_value_int32: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const n = this.load(valueIdx); | |
if (typeof n !== 'number') { | |
return NAPI_NUMBER_EXPECTED; | |
} | |
this.view.setInt32(resultPtr, n, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_int64: this.wrap(() => {}), | |
napi_get_value_string_latin1: this.wrap((envPtr, valueIdx, bufPtr, bufSize, resultPtr) => { | |
const s = this.load(valueIdx); | |
if (typeof s !== 'string') { | |
return NAPI_STRING_EXPECTED; | |
} | |
const written = Buffer.from(this.memory.buffer).write(s, bufPtr, bufSize, 'latin1'); | |
this.view.setUint32(resultPtr, written, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_string_utf8: this.wrap((envPtr, valueIdx, bufPtr, bufSize, resultPtr) => { | |
const s = this.load(valueIdx); | |
if (typeof s !== 'string') { | |
return NAPI_STRING_EXPECTED; | |
} | |
const written = Buffer.from(this.memory.buffer).write(s, bufPtr, bufSize, 'utf8'); | |
this.view.setUint32(resultPtr, written, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_string_utf16: this.wrap((envPtr, valueIdx, bufPtr, bufSize, resultPtr) => { | |
const s = this.load(valueIdx); | |
if (typeof s !== 'string') { | |
return NAPI_STRING_EXPECTED; | |
} | |
const written = Buffer.from(this.memory.buffer).write(s, bufPtr, bufSize, 'utf16'); | |
this.view.setUint32(resultPtr, written, true); | |
return NAPI_OK; | |
}), | |
napi_get_value_uint32: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const n = this.load(valueIdx); | |
if (typeof n !== 'number') { | |
return NAPI_NUMBER_EXPECTED; | |
} | |
this.view.setInt32(resultPtr, n, true); | |
return NAPI_OK; | |
}), | |
napi_get_boolean: this.wrap((envPtr, value, resultPtr) => { | |
this.writeValue(resultPtr, this.store(value)); | |
return NAPI_OK; | |
}), | |
napi_get_global: this.wrap((envPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store(global)); | |
return NAPI_OK; | |
}), | |
napi_get_null: this.wrap((envPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store(null)); | |
return NAPI_OK; | |
}), | |
napi_get_undefined: this.wrap((envPtr, resultPtr) => { | |
this.writeValue(resultPtr, this.store(undefined)); | |
return NAPI_OK; | |
}), | |
napi_coerce_to_bool: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(!!this.load(valueIdx))); | |
return NAPI_OK; | |
}), | |
napi_coerce_to_number: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(Number(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_coerce_to_object: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(Object(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_coerce_to_string: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(String(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_typeof: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const v = this.load(valueIdx); | |
let vt; | |
switch (typeof v) { | |
case 'undefined': | |
vt = NAPI_UNDEFINED; | |
break; | |
// null handled below | |
case 'boolean': | |
vt = NAPI_BOOLEAN; | |
break; | |
case 'number': | |
vt = NAPI_NUMBER; | |
break; | |
case 'string': | |
vt = NAPI_STRING; | |
break; | |
case 'symbol': | |
vt = NAPI_SYMBOL; | |
break; | |
case 'object': | |
if (v === null) { | |
vt = NAPI_NULL; | |
} else if (this.externalData.has(v)) { | |
vt = NAPI_EXTERNAL; | |
} else { | |
vt = NAPI_OBJECT; | |
} | |
break; | |
case 'function': | |
vt = NAPI_FUNCTION; | |
break; | |
// external handled above | |
case 'bigint': | |
vt = NAPI_BIGINT; | |
break; | |
default: | |
throw new RangeError(); | |
} | |
this.view.setUint32(resultPtr, vt, true); | |
return NAPI_OK; | |
}), | |
napi_instanceof: this.wrap((envPtr, objectIdx, constructorIdx, resultPtr) => { | |
const object = this.load(objectIdx); | |
const cons = this.load(constructorIdx); | |
this.view.setUint8(resultPtr, object instanceof cons); | |
return NAPI_OK; | |
}), | |
napi_is_array: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isArray(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_is_arraybuffer: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isArrayBuffer(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_is_date: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isDate(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_is_error: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isNativeError(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_is_typedarray: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isTypedArray(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_is_dataview: this.wrap((envPtr, valueIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(util.types.isDataView(this.load(valueIdx)))); | |
return NAPI_OK; | |
}), | |
napi_strict_equals: this.wrap((envPtr, lhsIdx, rhsIdx, resultPtr) => { | |
this.writeValue(resultPtr, this.store(this.load(lhsIdx) === this.load(rhsIdx))); | |
return NAPI_OK; | |
}), | |
napi_get_property_names: this.wrap((envPtr, valueIdx, resultPtr) => { | |
const object = this.load(valueIdx); | |
this.writeValue(resultPtr, this.store(Object.keys(object))); | |
return NAPI_OK; | |
}), | |
napi_set_property: this.wrap((envPtr, objectIdx, keyIdx, valueIdx) => { | |
const object = this.load(objectIdx); | |
const key = this.load(keyIdx); | |
const value = this.load(valueIdx); | |
object[key] = value; | |
return NAPI_OK; | |
}), | |
napi_get_property: this.wrap((envPtr, objectIdx, keyIdx, resultPtr) => { | |
const object = this.load(objectIdx); | |
const key = this.load(keyIdx); | |
this.writeValue(resultPtr, this.store(object[key])); | |
return NAPI_OK; | |
}), | |
napi_has_property: this.wrap((envPtr, objectIdx, keyIdx, resultPtr) => { | |
const object = this.load(objectIdx); | |
const key = this.load(keyIdx); | |
this.writeValue(resultPtr, this.store(key in object)); | |
return NAPI_OK; | |
}), | |
napi_delete_property: this.wrap((envPtr, objectIdx, keyIdx, resultPtr) => { | |
const object = this.load(objectIdx); | |
const key = this.load(keyIdx); | |
this.writeValue(resultPtr, this.store(delete object[key])); | |
return NAPI_OK; | |
}), | |
napi_has_own_property: this.wrap((envPtr, objectIdx, keyIdx, resultPtr) => { | |
const object = this.load(objectIdx); | |
const key = this.load(keyIdx); | |
if (typeof key !== 'string' || typeof key !== 'symbol') { | |
return NAPI_NAME_EXPECTED; | |
} | |
this.writeValue(resultPtr, this.store(Object.prototype.hasOwnProperty.call(object, key))); | |
return NAPI_OK; | |
}), | |
napi_set_named_property: this.wrap((envPtr, objectIdx, utf8NamePtr, valueIdx) => { | |
const object = this.load(objectIdx); | |
const name = this.readCStringFrom(utf8NamePtr); | |
const value = this.load(valueIdx); | |
object[name] = value; | |
return NAPI_OK; | |
}), | |
napi_get_named_property: this.wrap((envPtr, objectIdx, utf8NamePtr, resultPtr) => { | |
const object = this.load(objectIdx); | |
const name = this.readCStringFrom(utf8NamePtr); | |
this.writeValue(resultPtr, this.store(object[name])); | |
return NAPI_OK; | |
}), | |
napi_has_named_property: this.wrap((envPtr, objectIdx, utf8NamePtr, resultPtr) => { | |
const object = this.load(objectIdx); | |
const key = this.readCStringFrom(utf8NamePtr); | |
this.writeValue(resultPtr, this.store(Object.prototype.hasOwnProperty.call(object, key))); | |
return NAPI_OK; | |
}), | |
napi_set_element: this.wrap((envPtr, objectIdx, index, valueIdx) => { | |
this.load(objectIdx)[index] = this.load(valueIdx); | |
return NAPI_OK; | |
}), | |
napi_get_element: this.wrap((envPtr, objectIdx, index, resultPtr) => { | |
this.writeValue(resultPtr, this.store(this.load(objectIdx)[index])); | |
return NAPI_OK; | |
}), | |
napi_has_element: this.wrap((envPtr, objectIdx, index, resultPtr) => { | |
const object = this.load(objectIdx); | |
this.writeValue(resultPtr, this.store(Object.prototype.hasOwnProperty.call(object, index))); | |
return NAPI_OK; | |
}), | |
napi_delete_element: this.wrap((envPtr, objectIdx, index, resultPtr) => { | |
const object = this.load(objectIdx); | |
this.writeValue(resultPtr, this.store(delete object[index])); | |
return NAPI_OK; | |
}), | |
napi_define_properties: this.wrap((envPtr, objectIdx, propertyCount, propertiesPtr) => { | |
const object = this.load(objectIdx); | |
if (typeof object !== 'object' || object === null) { | |
return NAPI_OBJECT_EXPECTED; | |
} | |
this.readPropertyDescriptors(propertyCount, propertiesPtr) | |
.forEach(({ | |
method, | |
getter, | |
setter, | |
valueIdx, | |
dataPtr, | |
descriptor, | |
name, | |
}) => { | |
if (getter || setter) { | |
const get = getter ? this.createFunction(envPtr, getter, dataPtr) : undefined; | |
const set = setter ? this.createFunction(envPtr, setter, dataPtr) : undefined; | |
Object.defineProperty(object, name, { ...descriptor, get, set }); | |
} else if (method) { | |
const value = this.createFunction(envPtr, method, dataPtr, name); | |
Object.defineProperty(object, name, { ...descriptor, value }); | |
} else { | |
const value = this.load(valueIdx); | |
Object.defineProperty(object, name, { ...descriptor, value }); | |
} | |
}); | |
return NAPI_OK; | |
}), | |
napi_call_function: this.wrap((envPtr, recvIdx, funcIdx, argc, argvPtr, resultPtr) => { | |
const recv = this.load(recvIdx); | |
const func = this.load(funcIdx); | |
if (typeof func !== 'function') { | |
return NAPI_FUNCTION_EXPECTED; | |
} | |
const args = []; | |
for (let i = 0; i < argc; i += 1) { | |
args.push(this.load(argvPtr + (i * 4))); | |
} | |
const result = func.apply(recv, args); | |
this.writeValue(resultPtr, this.store(result)); | |
return NAPI_OK; | |
}), | |
napi_create_function: this.wrap((envPtr, utf8NamePtr, length, cb, data, resultPtr) => { | |
let name; | |
if (length > 0) { | |
name = Buffer.from(this.memory.buffer, utf8NamePtr, length).toString('utf8'); | |
} | |
const func = this.createFunction(envPtr, cb, data, name); | |
this.writeValue(resultPtr, this.store(func)); | |
return NAPI_OK; | |
}), | |
napi_get_cb_info: this.wrap((envPtr, cbinfoIdx, argcPtr, argvPtr, thisArgPtr, dataPtr) => { | |
const info = this.load(cbinfoIdx); | |
const argc = this.view.getUint32(argcPtr, true); | |
this.view.setUint32(argcPtr, info.args.length, true); | |
for (let i = 0; i < argc; i += 1) { | |
this.writeValue(argvPtr + (i * 4), this.store(info.args[i])); | |
} | |
this.writeValue(thisArgPtr, this.store(info.this)); | |
this.view.setUint32(dataPtr, info.data, true); | |
return NAPI_OK; | |
}), | |
napi_get_new_target: this.wrap((envPtr, cbinfoIdx, resultPtr) => { | |
const info = this.load(cbinfoIdx); | |
this.writeValue(resultPtr, this.store(info.newTarget)); | |
return NAPI_OK; | |
}), | |
napi_new_instance: this.wrap((envPtr, consIdx, argc, argvPtr, resultPtr) => { | |
const cons = this.load(consIdx); | |
if (typeof func !== 'function') { | |
return NAPI_FUNCTION_EXPECTED; | |
} | |
const args = []; | |
for (let i = 0; i < argc; i += 1) { | |
args.push(this.load(argvPtr + (i * 4))); | |
} | |
const result = Reflect.construct(cons, args); | |
this.writeValue(resultPtr, this.store(result)); | |
return NAPI_OK; | |
}), | |
napi_define_class: this.wrap((envPtr, utf8NamePtr, utf8NameLength, constructorIdx, | |
data, propertyCount, propertiesPtr, resultPtr) => { | |
const func = this.createFunction(envPtr, constructorIdx, data, utf8NameLength > 0 | |
? Buffer.from(this.memory.buffer, utf8NamePtr, utf8NameLength).toString('utf8') | |
: undefined, kFuncConstructor); | |
this.readPropertyDescriptors(propertyCount, propertiesPtr) | |
.forEach(({ | |
method, | |
getter, | |
setter, | |
valueIdx, | |
dataPtr, | |
descriptor, | |
name, | |
}) => { | |
const target = descriptor.static ? func : func.prototype; | |
if (getter || setter) { | |
const get = getter ? this.createFunction(envPtr, getter, dataPtr) : undefined; | |
const set = setter ? this.createFunction(envPtr, setter, dataPtr) : undefined; | |
Object.defineProperty(target, name, { ...descriptor, get, set }); | |
} else if (method) { | |
const value = this.createFunction(envPtr, method, dataPtr, name, kFuncMethod); | |
Object.defineProperty(target, name, { ...descriptor, value }); | |
} else { | |
const value = this.load(valueIdx); | |
Object.defineProperty(target, name, { ...descriptor, value }); | |
} | |
}); | |
this.writeValue(resultPtr, this.store(func)); | |
return NAPI_OK; | |
}), | |
napi_wrap: this.wrap((envPtr, jsObjectIdx, nativeObjectPtr, | |
finalizeCb, finalizeHint, resultPtr) => { | |
const jsObject = this.load(jsObjectIdx); | |
this.finalizationGroup | |
.register(jsObject, [finalizeCb, envPtr, nativeObjectPtr, finalizeHint], nativeObjectPtr); | |
this.wrapData.set(jsObject, nativeObjectPtr); | |
if (resultPtr !== 0) { | |
const ref = { value: jsObject, ref: 1 }; | |
this.references.push(ref); | |
this.writeValue(resultPtr, this.references.length - 1); | |
} | |
return NAPI_OK; | |
}), | |
napi_unwrap: this.wrap((envPtr, jsObjectIdx, resultPtr) => { | |
const jsObject = this.load(jsObjectIdx); | |
if (!this.wrapData.has(jsObject)) { | |
return NAPI_INVALID_ARG; | |
} | |
this.view.setUint32(resultPtr, this.wrapData.get(jsObject)); | |
return NAPI_OK; | |
}), | |
napi_remove_wrap: this.wrap((envPtr, jsObjectIdx, resultPtr) => { | |
const jsObject = this.load(jsObjectIdx); | |
if (!this.wrapData.has(jsObject)) { | |
return NAPI_INVALID_ARG; | |
} | |
const nativeObjectPtr = this.wrapData.get(jsObject); | |
this.wrapData.delete(jsObject); | |
this.finalizationGroup.unregister(nativeObjectPtr); | |
this.view.setUint32(resultPtr, nativeObjectPtr); | |
return NAPI_OK; | |
}), | |
napi_add_finalizer: this.wrap((envPtr, jsObjectIdx, nativeObjectPtr, | |
finalizeCb, finalizeHint, resultPtr) => { | |
const jsObject = this.load(jsObjectIdx); | |
this.finalizationGroup | |
.register(jsObject, [finalizeCb, envPtr, nativeObjectPtr, finalizeHint], nativeObjectPtr); | |
if (resultPtr !== 0) { | |
const ref = { value: jsObject, ref: 1 }; | |
this.references.push(ref); | |
this.writeValue(resultPtr, this.references.length - 1); | |
} | |
return NAPI_OK; | |
}), | |
}; | |
refreshMemory() { | |
if (this.view === undefined || this.view.byteLength === 0) { | |
this.view = new DataView(this.memory.buffer); | |
} | |
} | |
openHandleScope() { | |
this.handleScope.unshift([null, undefined, true, false, global]); | |
} | |
closeHandleScope() { | |
this.handleScope.shift(); | |
} | |
store(obj) { | |
if (obj === null) { | |
return 0; | |
} | |
if (obj === undefined) { | |
return 1; | |
} | |
if (obj === true) { | |
return 2; | |
} | |
if (obj === false) { | |
return 3; | |
} | |
if (obj === global) { | |
return 4; | |
} | |
const idx = this.handleScope[0].length; | |
this.handleScope[0].push(obj); | |
return idx; | |
} | |
load(idx) { | |
return this.handleScope[0][idx]; | |
} | |
writeValue(ptr, idx) { | |
this.view.setUint32(ptr, idx, true); | |
} | |
readCStringFrom(ptr) { | |
let length = 0; | |
while (this.view.getUint8(ptr + length) !== 0) { | |
length += 1; | |
} | |
return Buffer.from(this.memory.buffer, ptr, length).toString('utf8'); | |
} | |
init(instance) { | |
this.memory = instance.exports.memory; | |
this.indirectFunctionTable = instance.exports.__indirect_function_table; | |
this.openHandleScope(); | |
try { | |
const idx = instance.exports._napi_register(0, this.store({})); | |
return this.load(idx); | |
} finally { | |
this.closeHandleScope(); | |
if (this.exceptionPending) { | |
this.exceptionPending = false; | |
throw this.exception; // eslint-disable-line no-unsafe-finally | |
} | |
} | |
} | |
} | |
module.exports = NAPI; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment