Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Node CI

on: [push]

jobs:
build:
name: Test Node.js ${{ matrix.node-version }} on ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [10.x, 12.x, 14.x, 16.x]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v1

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: Print Node.js Version
run: node --version

- name: Install Dependencies
run: npm install
env:
CI: true

- name: Run "build" step
run: npm run build --if-present
env:
CI: true

- name: Run tests
run: npm test
env:
CI: true
30 changes: 0 additions & 30 deletions .travis.yml

This file was deleted.

9 changes: 1 addition & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
node-weak
=========
### Make weak references to JavaScript Objects.
[![Build Status](https://travis-ci.org/TooTallNate/node-weak.svg?branch=master)](https://travis-ci.org/TooTallNate/node-weak)
[![Build Status](https://ci.appveyor.com/api/projects/status/09lf09d1a5hm24bq?svg=true)](https://ci.appveyor.com/project/TooTallNate/node-weak)
[![Build Status](https://github.com/TooTallNate/node-weak/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-weak/actions?workflow=Node+CI)

On certain rarer occasions, you run into the need to be notified when a JavaScript
object is going to be garbage collected. This feature is exposed to V8's C++ API,
Expand Down Expand Up @@ -125,12 +124,6 @@ Checks to see if `ref` is a dead reference. Returns `true` if the original Objec
has already been GC'd, `false` otherwise.


### Boolean weak.isNearDeath(Weakref ref)

Checks to see if `ref` is "near death". This will be `true` exactly during the
weak reference callback function, and `false` any other time.


### Boolean weak.isWeakRef(Object obj)

Checks to see if `obj` is "weak reference" instance. Returns `true` if the
Expand Down
49 changes: 0 additions & 49 deletions appveyor.yml

This file was deleted.

52 changes: 18 additions & 34 deletions src/weakref.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,20 +128,26 @@ NAN_INDEX_DELETER(WeakIndexedPropertyDeleter) {
info.GetReturnValue().Set(!dead && Nan::Delete(obj, index).FromJust());
}

NAN_PROPERTY_ENUMERATOR(WeakNamedPropertyEnumerator) {
UNWRAP
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, ONLY_ENUMERABLE, IndexFilter::kSkipIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : Nan::GetPropertyNames(obj).ToLocalChecked());
#endif
Comment on lines +132 to +137

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken property enumeration

The WeakNamedPropertyEnumerator function incorrectly returns empty arrays when the target is dead (garbage collected). This breaks the JavaScript property enumeration contract where proxy objects should consistently enumerate their own properties regardless of target liveness. The dead ? Nan::New<Array>(0) branch should be removed to always return the proxy's actual property names, ensuring consistent behavior for downstream consumers like Object.keys(), for...in loops, and JSON serialization.

Code suggestion
Check the AI-generated fix before applying
Suggested change
UNWRAP
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, ONLY_ENUMERABLE, IndexFilter::kSkipIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : Nan::GetPropertyNames(obj).ToLocalChecked());
#endif
UNWRAP
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, ONLY_ENUMERABLE, IndexFilter::kSkipIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(Nan::GetPropertyNames(obj).ToLocalChecked());
#endif

Code Review Run #43c5fe


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

}

/**
* Only one "enumerator" function needs to be defined. This function is used for
* both the property and indexed enumerator functions.
*/

NAN_PROPERTY_ENUMERATOR(WeakPropertyEnumerator) {
NAN_INDEX_ENUMERATOR(WeakIndexedPropertyEnumerator) {
UNWRAP
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, static_cast<PropertyFilter> (ONLY_ENUMERABLE | SKIP_STRINGS | SKIP_SYMBOLS), IndexFilter::kIncludeIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : Nan::GetPropertyNames(obj).ToLocalChecked());
#endif
Comment on lines +142 to +146

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken indexed enumeration

The WeakIndexedPropertyEnumerator has the same critical issue as WeakNamedPropertyEnumerator - it returns empty arrays when the target is dead, breaking indexed property enumeration. This affects array-like behavior and indexed access patterns. The conditional dead check should be removed to maintain consistent enumeration behavior.

Code suggestion
Check the AI-generated fix before applying
Suggested change
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, static_cast<PropertyFilter> (ONLY_ENUMERABLE | SKIP_STRINGS | SKIP_SYMBOLS), IndexFilter::kIncludeIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(dead ? Nan::New<Array>(0) : Nan::GetPropertyNames(obj).ToLocalChecked());
#endif
#if NODE_MAJOR_VERSION >= 7
info.GetReturnValue().Set(obj->GetPropertyNames(Nan::GetCurrentContext(), KeyCollectionMode::kIncludePrototypes, static_cast<PropertyFilter> (ONLY_ENUMERABLE | SKIP_STRINGS | SKIP_SYMBOLS), IndexFilter::kIncludeIndices).ToLocalChecked());
#else
info.GetReturnValue().Set(Nan::GetPropertyNames(obj).ToLocalChecked());
#endif

Code Review Run #43c5fe


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

}

/**
* Weakref callback function. Invokes the "global" callback function,
* which emits the _CB event on the per-object EventEmitter.
* Weakref callback function. Invokes the "global" callback function.
*/

static void TargetCallback(const Nan::WeakCallbackInfo<proxy_container> &info) {
Expand All @@ -152,11 +158,7 @@ static void TargetCallback(const Nan::WeakCallbackInfo<proxy_container> &info) {
Local<Value> argv[] = {
Nan::New<Object>(cont->emitter)
};
// Invoke callback directly, not via Nan::Callback->Call() which uses
// node::MakeCallback() which calls into process._tickCallback()
// too. Those other callbacks are not safe to run from here.
v8::Local<v8::Function> globalCallbackDirect = globalCallback->GetFunction();
globalCallbackDirect->Call(Nan::GetCurrentContext()->Global(), 1, argv);
Nan::Call(*globalCallback, 1, argv);

// clean everything up
Local<Object> proxy = Nan::New<Object>(cont->proxy);
Expand All @@ -177,7 +179,7 @@ NAN_METHOD(Create) {

Local<Object> _target = info[0].As<Object>();
Local<Object> _emitter = info[1].As<Object>();
Local<Object> proxy = Nan::New<ObjectTemplate>(proxyClass)->NewInstance();
Local<Object> proxy = Nan::NewInstance(Nan::New<ObjectTemplate>(proxyClass)).ToLocalChecked();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong instantiation method

The change from Nan::New<ObjectTemplate>(proxyClass)->NewInstance() to Nan::NewInstance(Nan::New<ObjectTemplate>(proxyClass)).ToLocalChecked() is incorrect. Nan::NewInstance expects a FunctionTemplate or Function, not an ObjectTemplate. The original pattern using ->NewInstance() on the ObjectTemplate is the correct approach for instantiating objects from ObjectTemplates in NAN.

Code suggestion
Check the AI-generated fix before applying
Suggested change
Local<Object> proxy = Nan::NewInstance(Nan::New<ObjectTemplate>(proxyClass)).ToLocalChecked();
Local<Object> proxy = Nan::New<ObjectTemplate>(proxyClass)->NewInstance();

Code Review Run #43c5fe


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

cont->proxy.Reset(proxy);
cont->emitter.Reset(_emitter);
cont->target.Reset(_target);
Expand Down Expand Up @@ -224,23 +226,6 @@ NAN_METHOD(Get) {
info.GetReturnValue().Set(Unwrap(proxy));
}

/**
* `isNearDeath(weakref)` JS function.
*/

NAN_METHOD(IsNearDeath) {
WEAKREF_FIRST_ARG

proxy_container *cont = reinterpret_cast<proxy_container*>(
Nan::GetInternalFieldPointer(proxy, FIELD_INDEX_CONTAINER)
);
assert(cont != NULL);

Local<Boolean> rtn = Nan::New<Boolean>(cont->target.IsNearDeath());

info.GetReturnValue().Set(rtn);
}

/**
* `isDead(weakref)` JS function.
*/
Expand Down Expand Up @@ -282,18 +267,17 @@ NAN_MODULE_INIT(Initialize) {
WeakNamedPropertySetter,
WeakNamedPropertyQuery,
WeakNamedPropertyDeleter,
WeakPropertyEnumerator);
WeakNamedPropertyEnumerator);
Nan::SetIndexedPropertyHandler(p,
WeakIndexedPropertyGetter,
WeakIndexedPropertySetter,
WeakIndexedPropertyQuery,
WeakIndexedPropertyDeleter,
WeakPropertyEnumerator);
WeakIndexedPropertyEnumerator);
p->SetInternalFieldCount(FIELD_COUNT);

Nan::SetMethod(target, "get", Get);
Nan::SetMethod(target, "isWeakRef", IsWeakRef);
Nan::SetMethod(target, "isNearDeath", IsNearDeath);
Nan::SetMethod(target, "isDead", IsDead);
Nan::SetMethod(target, "_create", Create);
Nan::SetMethod(target, "_getEmitter", GetEmitter);
Expand Down
1 change: 0 additions & 1 deletion test/exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe('exports', function () {
checkFunction('get')
checkFunction('create')
checkFunction('isWeakRef')
checkFunction('isNearDeath')
checkFunction('isDead')
checkFunction('callbacks')
checkFunction('addCallback')
Expand Down