Protect realm and callback from GC in addNotification

Fixes #54
This commit is contained in:
Scott Kyle 2015-10-12 17:17:43 -07:00
parent f4715da2a4
commit d996147b09
2 changed files with 23 additions and 5 deletions

View File

@ -346,7 +346,7 @@ JSValueRef RealmWrite(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
try { try {
RJSValidateArgumentCount(argumentCount, 1); RJSValidateArgumentCount(argumentCount, 1);
JSObjectRef object = RJSValidatedValueToObject(ctx, arguments[0]); JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject); SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
realm->begin_transaction(); realm->begin_transaction();
JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException); JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException);
@ -370,6 +370,8 @@ JSValueRef RealmWrite(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
namespace realm { namespace realm {
struct Notification { struct Notification {
JSGlobalContextRef ctx; JSGlobalContextRef ctx;
JSObjectRef realmObject;
JSObjectRef callbackObject;
RJSRealmDelegate::NotificationFunction func; RJSRealmDelegate::NotificationFunction func;
}; };
} }
@ -378,22 +380,26 @@ JSValueRef RealmAddNotification(JSContextRef ctx, JSObjectRef function, JSObject
try { try {
RJSValidateArgumentCount(argumentCount, 1); RJSValidateArgumentCount(argumentCount, 1);
JSObjectRef user_function = RJSValidatedValueToObject(ctx, arguments[0]); JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[0]);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject); SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
JSGlobalContextRef gCtx = JSGlobalContextRetain(JSContextGetGlobalContext(ctx)); JSGlobalContextRef gCtx = JSGlobalContextRetain(JSContextGetGlobalContext(ctx));
JSValueProtect(gCtx, thisObject);
JSValueProtect(gCtx, callback);
RJSRealmDelegate::NotificationFunction func = std::make_shared<std::function<void(const std::string)>>([=](std::string notification_name) { RJSRealmDelegate::NotificationFunction func = std::make_shared<std::function<void(const std::string)>>([=](std::string notification_name) {
JSValueRef arguments[2]; JSValueRef arguments[2];
arguments[0] = thisObject; arguments[0] = thisObject;
arguments[1] = RJSValueForString(gCtx, notification_name); arguments[1] = RJSValueForString(gCtx, notification_name);
JSValueRef ex = NULL; JSValueRef ex = NULL;
JSObjectCallAsFunction(gCtx, user_function, thisObject, 2, arguments, &ex); JSObjectCallAsFunction(gCtx, callback, thisObject, 2, arguments, &ex);
if (ex) { if (ex) {
throw RJSException(gCtx, ex); throw RJSException(gCtx, ex);
} }
}); });
static_cast<RJSRealmDelegate *>(realm->m_delegate.get())->add_notification(func); static_cast<RJSRealmDelegate *>(realm->m_delegate.get())->add_notification(func);
return RJSWrapObject<Notification *>(ctx, RJSNotificationClass(), new Notification { gCtx, func }); return RJSWrapObject<Notification *>(ctx, RJSNotificationClass(), new Notification { gCtx, thisObject, callback, func });
} }
catch (std::exception &exp) { catch (std::exception &exp) {
if (jsException) { if (jsException) {
@ -420,7 +426,11 @@ JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb
void RJSNotificationFinalize(JSObjectRef object) { void RJSNotificationFinalize(JSObjectRef object) {
Notification *notification = RJSGetInternal<Notification *>(object); Notification *notification = RJSGetInternal<Notification *>(object);
JSGlobalContextRelease(notification->ctx); JSGlobalContextRef ctx = notification->ctx;
JSValueUnprotect(ctx, notification->callbackObject);
JSValueUnprotect(ctx, notification->realmObject);
JSGlobalContextRelease(ctx);
RJSFinalize<Notification *>(object); RJSFinalize<Notification *>(object);
} }

View File

@ -106,6 +106,14 @@ static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef
return object; return object;
} }
static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = NULL) {
JSObjectRef object = JSValueToObject(ctx, value, NULL);
if (!object || !JSObjectIsFunction(ctx, object)) {
throw std::runtime_error(message ?: "Value is not a function.");
}
return object;
}
static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef value) { static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef value) {
JSValueRef exception = NULL; JSValueRef exception = NULL;
double number = JSValueToNumber(ctx, value, &exception); double number = JSValueToNumber(ctx, value, &exception);