-
Notifications
You must be signed in to change notification settings - Fork 464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fixes v8 GC access violation for zombie objects #638
Changes from 1 commit
7c59fab
beb4920
80d6607
41c7a89
253a9e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
added class FinilizerHint friend to CallbackInfo in order to hide the implementation from user API enabled tests for non exception enabled builds
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2711,6 +2711,27 @@ inline Object FunctionReference::New(const std::vector<napi_value>& args) const | |||||||||||
return scope.Escape(Value().New(args)).As<Object>(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
class FinalizerHint { | ||||||||||||
private: | ||||||||||||
FinalizerHint() {} | ||||||||||||
public: | ||||||||||||
bool shouldFinalize; | ||||||||||||
|
||||||||||||
static void Init(CallbackInfo& info, bool shouldFinalize = true) { | ||||||||||||
info.finalizerHint = new FinalizerHint(); | ||||||||||||
info.finalizerHint->shouldFinalize = shouldFinalize; | ||||||||||||
} | ||||||||||||
|
||||||||||||
static void Clear(CallbackInfo& info) { | ||||||||||||
info.finalizerHint = nullptr; | ||||||||||||
} | ||||||||||||
|
||||||||||||
static FinalizerHint* Get(const CallbackInfo& info) { | ||||||||||||
return info.finalizerHint; | ||||||||||||
} | ||||||||||||
}; | ||||||||||||
|
||||||||||||
|
||||||||||||
//////////////////////////////////////////////////////////////////////////////// | ||||||||||||
// CallbackInfo class | ||||||||||||
//////////////////////////////////////////////////////////////////////////////// | ||||||||||||
|
@@ -3120,7 +3141,12 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) { | |||||||||||
napi_status status; | ||||||||||||
napi_ref ref; | ||||||||||||
T* instance = static_cast<T*>(this); | ||||||||||||
status = napi_wrap(env, wrapper, instance, FinalizeCallback, (void*)callbackInfo.zombie, &ref); | ||||||||||||
FinalizerHint* finalizerHint = FinalizerHint::Get(callbackInfo); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The hint is not optional. It should die if it's absent. |
||||||||||||
status = napi_wrap(env, wrapper, instance, FinalizeCallback, (void*)finalizerHint, &ref); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid C-style casts! |
||||||||||||
if (status != napi_ok && finalizerHint != nullptr) { | ||||||||||||
FinalizerHint::Clear(const_cast<Napi::CallbackInfo&>(callbackInfo)); | ||||||||||||
delete finalizerHint; | ||||||||||||
} | ||||||||||||
NAPI_THROW_IF_FAILED_VOID(env, status); | ||||||||||||
gabrielschulhof marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
|
||||||||||||
Reference<Object>* instanceRef = instance; | ||||||||||||
|
@@ -3699,21 +3725,26 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper( | |||||||||||
T* instance; | ||||||||||||
napi_value wrapper = details::WrapCallback([&] () -> napi_value { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We don't need this change, because it's OK to return |
||||||||||||
CallbackInfo callbackInfo(env, info); | ||||||||||||
callbackInfo.zombie = new Zombie(); | ||||||||||||
FinalizerHint::Init(callbackInfo); | ||||||||||||
#ifdef NAPI_CPP_EXCEPTIONS | ||||||||||||
try { | ||||||||||||
instance = new T(callbackInfo); | ||||||||||||
return callbackInfo.This(); | ||||||||||||
} | ||||||||||||
catch (...) { | ||||||||||||
callbackInfo.zombie->isZombie = true; | ||||||||||||
FinalizerHint* finalizerHint = FinalizerHint::Get(callbackInfo); | ||||||||||||
if (finalizerHint != nullptr) { | ||||||||||||
finalizerHint->shouldFinalize = false; | ||||||||||||
} | ||||||||||||
throw; | ||||||||||||
} | ||||||||||||
#else | ||||||||||||
instance = new T(callbackInfo); | ||||||||||||
if (callbackInfo.Env().IsExceptionPending()) { | ||||||||||||
callbackInfo.zombie->isZombie = true; | ||||||||||||
return nullptr; | ||||||||||||
FinalizerHint* finalizerHint = FinalizerHint::Get(callbackInfo); | ||||||||||||
if (finalizerHint != nullptr) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check should be a hard check. If |
||||||||||||
finalizerHint->shouldFinalize = false; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
return callbackInfo.This(); | ||||||||||||
#endif | ||||||||||||
|
@@ -3841,10 +3872,10 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper( | |||||||||||
template <typename T> | ||||||||||||
inline void ObjectWrap<T>::FinalizeCallback(napi_env env, void* data, void* hint) { | ||||||||||||
if (hint != nullptr) { | ||||||||||||
Zombie* zombie = (Zombie*)hint; | ||||||||||||
bool isZombie = zombie->isZombie; | ||||||||||||
delete zombie; | ||||||||||||
if (isZombie) { | ||||||||||||
FinalizerHint* finalizerHint = (FinalizerHint*)hint; | ||||||||||||
bool shouldFinalize = finalizerHint->shouldFinalize; | ||||||||||||
delete finalizerHint; | ||||||||||||
if (!shouldFinalize) { | ||||||||||||
return; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1396,11 +1396,8 @@ namespace Napi { | |
RangeError(napi_env env, napi_value value); | ||
}; | ||
|
||
struct Zombie { | ||
bool isZombie = false; | ||
}; | ||
|
||
class CallbackInfo { | ||
friend class FinalizerHint; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to forward-declare this class at the top of the file. |
||
public: | ||
CallbackInfo(napi_env env, napi_callback_info info); | ||
~CallbackInfo(); | ||
|
@@ -1418,8 +1415,6 @@ namespace Napi { | |
void* Data() const; | ||
void SetData(void* data); | ||
|
||
Zombie* zombie; | ||
|
||
private: | ||
const size_t _staticArgCount = 6; | ||
napi_env _env; | ||
|
@@ -1430,6 +1425,7 @@ namespace Napi { | |
napi_value _staticArgs[6]; | ||
napi_value* _dynamicArgs; | ||
void* _data; | ||
FinalizerHint* finalizerHint; | ||
}; | ||
|
||
class PropertyDescriptor { | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -258,12 +258,10 @@ const test = (binding) => { | |||
|
||||
const testConstructorExceptions = () => { | ||||
const TestConstructorExceptions = binding.objectwrap.TestConstructorExceptions; | ||||
if (TestConstructorExceptions) { | ||||
console.log("Runnig test testConstructorExceptions"); | ||||
assert.throws(() => { new TestConstructorExceptions(); }); | ||||
global.gc(); | ||||
console.log("Test testConstructorExceptions complete"); | ||||
} | ||||
console.log("Runnig test testConstructorExceptions"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The console log is unnecessary. |
||||
assert.throws(() => { new TestConstructorExceptions(); }); | ||||
global.gc(); | ||||
console.log("Test testConstructorExceptions complete"); | ||||
} | ||||
|
||||
// `Test` is needed for accessing exposed symbols | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use non-const references in Node.js. Only const references and pointers. So, please turn this into a pointer!