@gjsify/napi 0.64.2 → 0.64.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/cc/common.h +8 -1
- package/src/cc/env.cc +88 -1
- package/src/cc/stubs.cc +1 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/napi",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.5",
|
|
4
4
|
"description": "Node-API (N-API) host for GJS — implements the napi C ABI over SpiderMonkey (mozjs-140) so GJS can load unmodified compiled .node addons. The forward mirror of @gjsify/node-gi (GI in Node); this is N-API in GJS. Vala/C++ shim as .so+.gir+.typelib prebuild, loaded via imports.gi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/esm/index.js",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
},
|
|
98
98
|
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/napi/napi#readme",
|
|
99
99
|
"optionalDependencies": {
|
|
100
|
-
"@gjsify/napi-darwin-arm64": "0.64.
|
|
101
|
-
"@gjsify/napi-linux-x64": "0.64.
|
|
100
|
+
"@gjsify/napi-darwin-arm64": "0.64.5",
|
|
101
|
+
"@gjsify/napi-linux-x64": "0.64.5"
|
|
102
102
|
}
|
|
103
103
|
}
|
package/src/cc/common.h
CHANGED
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
#include <js/Exception.h>
|
|
58
58
|
#include <js/GCAPI.h>
|
|
59
59
|
#include <js/GlobalObject.h>
|
|
60
|
+
#include <js/MemoryFunctions.h>
|
|
60
61
|
#include <js/PropertyAndElement.h>
|
|
61
62
|
#include <js/RootingAPI.h>
|
|
62
63
|
#include <js/String.h>
|
|
@@ -423,6 +424,11 @@ struct napi_env__ {
|
|
|
423
424
|
// Foreign-object InstanceState WeakMap (§5d fallback tier), lazily
|
|
424
425
|
// created. Reset in teardown().
|
|
425
426
|
JS::PersistentRooted<JSObject*> wrap_map;
|
|
427
|
+
// Net external memory the addon has reported
|
|
428
|
+
// (napi_adjust_external_memory). Mirrors exactly what is booked against
|
|
429
|
+
// `global` via JS::AddAssociatedMemory, so teardown can hand that amount
|
|
430
|
+
// back — see env.cc.
|
|
431
|
+
int64_t external_memory = 0;
|
|
426
432
|
|
|
427
433
|
// §5b ref lists (sentinel heads; two-list teardown order per
|
|
428
434
|
// js_native_api_v8.h:121-130).
|
|
@@ -466,7 +472,8 @@ struct napi_env__ {
|
|
|
466
472
|
// 3. RefTracker::FinalizeAll(finalizing_reflist) then (reflist);
|
|
467
473
|
// 4. instance-data finalizer;
|
|
468
474
|
// 5. flip can_call_into_js/torn_down (subsequent napi entry → status);
|
|
469
|
-
// 6.
|
|
475
|
+
// 6. return the still-booked external memory, then drop every root:
|
|
476
|
+
// remove the extra-roots tracer, reset
|
|
470
477
|
// PersistentRooteds, clear arena/scopes/weak set — all BEFORE
|
|
471
478
|
// JS_DestroyContext runs (a live root past that point is the UAF
|
|
472
479
|
// class). Callback bundles are NOT freed here: live function
|
package/src/cc/env.cc
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// @gjsify/napi — env lifecycle, handle arena, GC tracer, handle scopes
|
|
2
|
+
// @gjsify/napi — env lifecycle, handle arena, GC tracer, handle scopes,
|
|
3
|
+
// external-memory accounting.
|
|
3
4
|
//
|
|
4
5
|
// Reference: refs/node/src/js_native_api_v8.{h,cc} (Node.js contributors,
|
|
5
6
|
// MIT) for the napi_env/handle-scope semantics; tracer pattern adapted from
|
|
@@ -162,6 +163,20 @@ void napi_env__::teardown() {
|
|
|
162
163
|
// context, refs/gjs/gjs/context.cpp:457.)
|
|
163
164
|
JS_RemoveExtraGCRootsTracer(cx, gjsify_napi::trace_env, this);
|
|
164
165
|
exports.reset();
|
|
166
|
+
// Hand back whatever external memory is still booked against the global
|
|
167
|
+
// BEFORE that root goes: an Add without its Remove leaves the zone's
|
|
168
|
+
// malloc-bytes counter inflated for the rest of the process. Finalizers
|
|
169
|
+
// ran in steps 2-4, so everything they returned is already subtracted;
|
|
170
|
+
// what is left is what the addon never gave back.
|
|
171
|
+
if (external_memory > 0 && global.get() != nullptr) {
|
|
172
|
+
g_debug("gjsify-napi: returning %" G_GINT64_FORMAT
|
|
173
|
+
" bytes of external memory at env teardown (%s)",
|
|
174
|
+
external_memory, filename.c_str());
|
|
175
|
+
JS::RemoveAssociatedMemory(global.get(),
|
|
176
|
+
static_cast<size_t>(external_memory),
|
|
177
|
+
JS::MemoryUse::Embedding1);
|
|
178
|
+
}
|
|
179
|
+
external_memory = 0;
|
|
165
180
|
global.reset();
|
|
166
181
|
wrap_map.reset();
|
|
167
182
|
arena.clearAll();
|
|
@@ -260,3 +275,75 @@ napi_status NAPI_CDECL napi_escape_handle(napi_env env,
|
|
|
260
275
|
*result = reinterpret_cast<napi_value>(rec.escape_slot);
|
|
261
276
|
return gjsify_napi::clear_last_error(env);
|
|
262
277
|
}
|
|
278
|
+
|
|
279
|
+
// ---- external memory (js_native_api_v8.cc:3043-3052 semantics) ----
|
|
280
|
+
|
|
281
|
+
// Node hands the delta straight to V8's
|
|
282
|
+
// AdjustAmountOfExternalAllocatedMemory, which feeds the GC's allocation-
|
|
283
|
+
// pressure heuristic and returns the new total. mozjs's equivalent pair,
|
|
284
|
+
// JS::Add/RemoveAssociatedMemory, books the amount against an OBJECT — and
|
|
285
|
+
// this Node signature carries none. The realm global is the one object
|
|
286
|
+
// guaranteed to outlive every allocation the addon reports (it dies with the
|
|
287
|
+
// context, and teardown hands the balance back), so the env's whole external
|
|
288
|
+
// total hangs off it under MemoryUse::Embedding1. GJS itself never uses that
|
|
289
|
+
// enumerator (it books Cairo, GObjectInstanceStruct and GdkPixbuf), so the
|
|
290
|
+
// slot is ours alone and a mismatched Remove cannot corrupt GJS's own
|
|
291
|
+
// accounting.
|
|
292
|
+
//
|
|
293
|
+
// The clamp at zero is OURS, and the reason is mozjs, not Node parity:
|
|
294
|
+
// RemoveAssociatedMemory must never give back more than was booked for a
|
|
295
|
+
// given (object, use) pair — that asserts in a debug mozjs and silently
|
|
296
|
+
// wrecks the zone's malloc-bytes accounting in a release one. So the running
|
|
297
|
+
// balance lives in the env and every delta is applied to it, never passed
|
|
298
|
+
// through raw.
|
|
299
|
+
//
|
|
300
|
+
// Node does NOT tolerate an over-subtract either, it just fails harder:
|
|
301
|
+
// V8's Heap::UpdateExternalMemory keeps the total in a uint64_t and guards it
|
|
302
|
+
// with CHECK_GE(total_before, -delta) (refs/node/deps/v8/src/heap/heap.cc),
|
|
303
|
+
// a release-mode check — an addon that returns more than it booked ABORTS
|
|
304
|
+
// node. Node's total is therefore never negative, which is why a single
|
|
305
|
+
// non-negative balance is the Node-faithful shape; reporting a clamped zero
|
|
306
|
+
// instead of aborting is the deliberate, strictly softer deviation.
|
|
307
|
+
//
|
|
308
|
+
// The INT64_MAX saturation is likewise ours and likewise softer: V8 CHECKs
|
|
309
|
+
// that a single delta is under --external-memory-max-reasonable-size (32 GB
|
|
310
|
+
// by default, api.cc AdjustAmountOfExternalAllocatedMemoryImpl) and aborts
|
|
311
|
+
// past it. Saturating keeps a nonsensical delta from wrapping the counter.
|
|
312
|
+
//
|
|
313
|
+
// Basic env: callable from a synchronous finalizer, i.e. during GC. No JS
|
|
314
|
+
// runs and no exception state is touched, so CHECK_ENV is the whole gate, as
|
|
315
|
+
// in Node. After teardown the global root is gone and only the counter moves,
|
|
316
|
+
// so a late call still answers instead of touching a dropped root.
|
|
317
|
+
napi_status NAPI_CDECL napi_adjust_external_memory(node_api_basic_env basic_env,
|
|
318
|
+
int64_t change_in_bytes,
|
|
319
|
+
int64_t* adjusted_value) {
|
|
320
|
+
napi_env env = const_cast<napi_env>(basic_env);
|
|
321
|
+
GJSIFY_NAPI_CHECK_ENV(env);
|
|
322
|
+
GJSIFY_NAPI_CHECK_ARG(env, adjusted_value);
|
|
323
|
+
|
|
324
|
+
const int64_t booked = env->external_memory;
|
|
325
|
+
int64_t target;
|
|
326
|
+
if (change_in_bytes > 0 && booked > INT64_MAX - change_in_bytes) {
|
|
327
|
+
target = INT64_MAX;
|
|
328
|
+
} else {
|
|
329
|
+
target = booked + change_in_bytes;
|
|
330
|
+
if (target < 0) {
|
|
331
|
+
target = 0;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
JSObject* owner = env->global.get();
|
|
336
|
+
if (owner != nullptr && target != booked) {
|
|
337
|
+
if (target > booked) {
|
|
338
|
+
JS::AddAssociatedMemory(owner, static_cast<size_t>(target - booked),
|
|
339
|
+
JS::MemoryUse::Embedding1);
|
|
340
|
+
} else {
|
|
341
|
+
JS::RemoveAssociatedMemory(owner,
|
|
342
|
+
static_cast<size_t>(booked - target),
|
|
343
|
+
JS::MemoryUse::Embedding1);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
env->external_memory = target;
|
|
347
|
+
*adjusted_value = target;
|
|
348
|
+
return gjsify_napi::clear_last_error(env);
|
|
349
|
+
}
|
package/src/cc/stubs.cc
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// napi_*/node_api_* references always resolve — reaching past the
|
|
11
11
|
// implemented surface fails loud with napi_generic_failure + last_error
|
|
12
12
|
// (plan section 2 posture), never with an unresolved-symbol load failure and
|
|
13
|
-
// never with a crash.
|
|
13
|
+
// never with a crash. 9 stubs.
|
|
14
14
|
|
|
15
15
|
#include "common.h"
|
|
16
16
|
|
|
@@ -52,12 +52,6 @@ napi_status NAPI_CDECL napi_create_external_arraybuffer(napi_env env, void* exte
|
|
|
52
52
|
GJSIFY_NAPI_STUB(env);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
napi_status NAPI_CDECL napi_adjust_external_memory(node_api_basic_env env, int64_t change_in_bytes, int64_t* adjusted_value) {
|
|
56
|
-
(void)change_in_bytes;
|
|
57
|
-
(void)adjusted_value;
|
|
58
|
-
GJSIFY_NAPI_STUB(env);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
napi_status NAPI_CDECL napi_create_bigint_words(napi_env env, int sign_bit, size_t word_count, const uint64_t* words, napi_value* result) {
|
|
62
56
|
(void)sign_bit;
|
|
63
57
|
(void)word_count;
|