Skip to content

Commit 669ca33

Browse files
authored
fix: Ensure Isolate is alive before using it (#63)
1 parent ee5ee85 commit 669ca33

File tree

5 files changed

+96
-34
lines changed

5 files changed

+96
-34
lines changed

NativeScript/runtime/ArgConverter.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ObjectManager.h"
77
#include "Interop.h"
88
#include "Helpers.h"
9+
#include "Runtime.h"
910

1011
using namespace v8;
1112
using namespace std;
@@ -91,6 +92,12 @@
9192
MethodCallbackWrapper* data = static_cast<MethodCallbackWrapper*>(userData);
9293

9394
Isolate* isolate = data->isolate_;
95+
96+
if (!Runtime::IsAlive(isolate)) {
97+
memset(retValue, 0, cif->rtype->size);
98+
return;
99+
}
100+
94101
v8::Locker locker(isolate);
95102
Isolate::Scope isolate_scope(isolate);
96103
HandleScope handle_scope(isolate);

NativeScript/runtime/ClassBuilder.mm

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Helpers.h"
1111
#include "Caches.h"
1212
#include "Interop.h"
13+
#include "Runtime.h"
1314

1415
using namespace v8;
1516

@@ -254,45 +255,49 @@
254255
/// in order to make both of them destroyable/GC-able. When the JavaScript object is GC-ed we release the native counterpart as well.
255256
void (*retain)(id, SEL) = (void (*)(id, SEL))FindNotOverridenMethod(extendedClass, @selector(retain));
256257
IMP newRetain = imp_implementationWithBlock(^(id self) {
257-
if ([self retainCount] == 1) {
258-
auto it = cache->Instances.find(self);
259-
if (it != cache->Instances.end()) {
260-
v8::Locker locker(isolate);
261-
Isolate::Scope isolate_scope(isolate);
262-
HandleScope handle_scope(isolate);
263-
Local<Value> value = it->second->Get(isolate);
264-
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
265-
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
266-
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
267-
objcWrapper->GcProtect();
268-
}
269-
}
270-
}
271-
272-
return retain(self, @selector(retain));
258+
if ([self retainCount] == 1) {
259+
auto it = cache->Instances.find(self);
260+
if (it != cache->Instances.end()) {
261+
v8::Locker locker(isolate);
262+
Isolate::Scope isolate_scope(isolate);
263+
HandleScope handle_scope(isolate);
264+
Local<Value> value = it->second->Get(isolate);
265+
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
266+
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
267+
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
268+
objcWrapper->GcProtect();
269+
}
270+
}
271+
}
272+
273+
return retain(self, @selector(retain));
273274
});
274275
class_addMethod(extendedClass, @selector(retain), newRetain, "@@:");
275276

276277
void (*release)(id, SEL) = (void (*)(id, SEL))FindNotOverridenMethod(extendedClass, @selector(release));
277278
IMP newRelease = imp_implementationWithBlock(^(id self) {
278-
if ([self retainCount] == 2) {
279-
auto it = cache->Instances.find(self);
280-
if (it != cache->Instances.end()) {
281-
v8::Locker locker(isolate);
282-
Isolate::Scope isolate_scope(isolate);
283-
HandleScope handle_scope(isolate);
284-
if (it->second != nullptr) {
285-
Local<Value> value = it->second->Get(isolate);
286-
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
287-
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
288-
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
289-
objcWrapper->GcUnprotect();
290-
}
291-
}
292-
}
293-
}
294-
295-
release(self, @selector(release));
279+
if (!Runtime::IsAlive(isolate)) {
280+
return;
281+
}
282+
283+
if ([self retainCount] == 2) {
284+
auto it = cache->Instances.find(self);
285+
if (it != cache->Instances.end()) {
286+
v8::Locker locker(isolate);
287+
Isolate::Scope isolate_scope(isolate);
288+
HandleScope handle_scope(isolate);
289+
if (it->second != nullptr) {
290+
Local<Value> value = it->second->Get(isolate);
291+
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
292+
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
293+
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
294+
objcWrapper->GcUnprotect();
295+
}
296+
}
297+
}
298+
}
299+
300+
release(self, @selector(release));
296301
});
297302
class_addMethod(extendedClass, @selector(release), newRelease, "v@:");
298303

NativeScript/runtime/Runtime.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ class Runtime {
4242
}
4343

4444
static id GetAppConfigValue(std::string key);
45+
46+
static bool IsAlive(v8::Isolate* isolate);
4547
private:
4648
static thread_local Runtime* currentRuntime_;
4749
static std::shared_ptr<v8::Platform> platform_;
50+
static std::vector<v8::Isolate*> isolates_;
4851
static bool mainThreadInitialized_;
4952

5053
void DefineGlobalObject(v8::Local<v8::Context> context);

NativeScript/runtime/Runtime.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
this->isolate_->Exit();
4949
}
5050

51+
Runtime::isolates_.erase(std::remove(Runtime::isolates_.begin(), Runtime::isolates_.end(), this->isolate_), Runtime::isolates_.end());
52+
5153
this->isolate_->Dispose();
5254

5355
currentRuntime_ = nullptr;
@@ -71,6 +73,8 @@
7173
create_params.array_buffer_allocator = &allocator_;
7274
Isolate* isolate = Isolate::New(create_params);
7375

76+
Runtime::isolates_.emplace_back(isolate);
77+
7478
return isolate;
7579
}
7680

@@ -226,7 +230,12 @@
226230
globalTemplate->Set(ToV8String(isolate, "__time"), timeFunctionTemplate);
227231
}
228232

233+
bool Runtime::IsAlive(Isolate* isolate) {
234+
return std::find(Runtime::isolates_.begin(), Runtime::isolates_.end(), isolate) != Runtime::isolates_.end();
235+
}
236+
229237
std::shared_ptr<Platform> Runtime::platform_;
238+
std::vector<Isolate*> Runtime::isolates_;
230239
bool Runtime::mainThreadInitialized_ = false;
231240
thread_local Runtime* Runtime::currentRuntime_ = nullptr;
232241

TestRunner/app/tests/shared/Workers/index.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,44 @@ describe("TNS Workers", () => {
497497
task.resume();
498498
` });
499499
});
500+
501+
it("Should not crash if the worker registers a notification", done => {
502+
var worker = new Worker("./tests/shared/Workers/EvalWorker.js");
503+
worker.onmessage = (msg) => {
504+
worker.terminate();
505+
NSNotificationCenter.defaultCenter.postNotificationNameObject("MyNotification", null);
506+
done();
507+
};
508+
509+
var workerScript = `
510+
var NotificationObserver = /** @class */ (function (_super) {
511+
__extends(NotificationObserver, _super);
512+
function NotificationObserver() {
513+
return _super !== null && _super.apply(this, arguments) || this;
514+
}
515+
NotificationObserver.initWithCallback = function (onReceiveCallback) {
516+
var observer = _super.new.call(this);
517+
observer._onReceiveCallback = onReceiveCallback;
518+
return observer;
519+
};
520+
NotificationObserver.prototype.onReceive = function (notification) {
521+
this._onReceiveCallback(notification);
522+
};
523+
NotificationObserver.ObjCExposedMethods = {
524+
onReceive: { returns: interop.types.void, params: [NSNotification] },
525+
};
526+
return NotificationObserver;
527+
}(NSObject));
528+
529+
const observer = NotificationObserver.initWithCallback(notification => { });
530+
531+
NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(observer, "onReceive", "MyNotification", null);
532+
533+
postMessage(self === global);
534+
`;
535+
536+
worker.postMessage({ eval: workerScript });
537+
});
500538
}
501539

502540
function generateRandomString(strLen) {

0 commit comments

Comments
 (0)