From 20bac8e2c7ba6d156282ab21218a0948e19c5ef1 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 25 Mar 2016 12:13:49 -0700 Subject: [PATCH 01/48] refactor list --- src/js_list.cpp | 141 ++++++++++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 59 deletions(-) diff --git a/src/js_list.cpp b/src/js_list.cpp index 6a1321f9..79eb1606 100644 --- a/src/js_list.cpp +++ b/src/js_list.cpp @@ -31,6 +31,26 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm; + +void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { + returnObject = JSValueMakeUndefined(ctx); +} + +template +void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { + returnObject = JSValueMakeNumber(ctx, number); +} + +void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { + returnObject = JSObjectMakeArray(ctx, count, objects, NULL); +} + +void RJSSetException(JSContextRef ctx, JSValueRef * &exceptionObject, std::exception &exception) { + if (exceptionObject) { + *exceptionObject = RJSMakeError(ctx, exception); + } +} + JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { try { List *list = RJSGetInternal(object); @@ -93,84 +113,80 @@ void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccum } } -JSValueRef ListPush(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); for (size_t i = 0; i < argumentCount; i++) { list->add(ctx, arguments[i]); } - return JSValueMakeNumber(ctx, list->size()); + RJSSetReturnNumber(ctx, returnObject, list->size()); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListPop(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); - + size_t size = list->size(); if (size == 0) { list->verify_in_transaction(); - return JSValueMakeUndefined(ctx); + RJSSetReturnUndefined(ctx, returnObject); } - size_t index = size - 1; - JSValueRef obj = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); - list->remove(index); - return obj; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); + else { + size_t index = size - 1; + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + list->remove(index); } } - return NULL; + catch (std::exception &exception) { + RJSSetException(ctx, exceptionObject, exception); + } } -JSValueRef ListUnshift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + +template +void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); for (size_t i = 0; i < argumentCount; i++) { list->insert(ctx, arguments[i], i); } - return JSValueMakeNumber(ctx, list->size()); + RJSSetReturnNumber(ctx, returnObject, list->size()); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListShift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); if (list->size() == 0) { list->verify_in_transaction(); - return JSValueMakeUndefined(ctx); + RJSSetReturnUndefined(ctx, returnObject); + } + else { + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); + list->remove(0); } - JSValueRef obj = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); - list->remove(0); - return obj; } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListSplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); size_t size = list->size(); @@ -190,7 +206,7 @@ JSValueRef ListSplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb remove = std::min(remove, size - index); } - std::vector removedObjects(remove); + std::vector removedObjects(remove); for (size_t i = 0; i < remove; i++) { removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); list->remove(index); @@ -198,63 +214,70 @@ JSValueRef ListSplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisOb for (size_t i = 2; i < argumentCount; i++) { list->insert(ctx, arguments[i], index + i - 2); } - return JSObjectMakeArray(ctx, remove, removedObjects.data(), jsException); + RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListStaticResults(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + +template +void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); - - return RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); + returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListFiltered(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); - return RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); + returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } -JSValueRef ListSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentRange(argumentCount, 1, 2); SharedRealm sharedRealm = *RJSGetInternal(thisObject); - return RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); + returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } +#define LIST_METHOD(METHOD_NAME) \ +JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ + JSValueRef returnObject = NULL; \ + METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, jsException); \ + return returnObject; \ +} + +LIST_METHOD(ListPush) +LIST_METHOD(ListPop) +LIST_METHOD(ListUnshift) +LIST_METHOD(ListShift) +LIST_METHOD(ListSplice) +LIST_METHOD(ListStaticResults) +LIST_METHOD(ListFiltered) +LIST_METHOD(ListSorted) + JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { return RJSWrapObject(ctx, RJSListClass(), new List(list)); } From 9626842f8ecc6f51ff49db2b5f31d2f359343b55 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 28 Mar 2016 13:21:36 -0700 Subject: [PATCH 02/48] start refactoring realm --- src/ios/RealmJS.xcodeproj/project.pbxproj | 10 +- src/js_compat.hpp | 68 +++++ src/js_init.cpp | 2 +- src/js_list.cpp | 300 ---------------------- src/js_list.hpp | 163 +++++++++++- src/js_object.cpp | 4 +- src/js_realm.cpp | 204 ++++++--------- src/js_util.hpp | 69 +++++ src/jsc/jsc_list.cpp | 117 +++++++++ src/jsc/jsc_list.hpp | 25 ++ src/jsc/jsc_util.hpp | 251 ++++++++++++++++++ src/rpc.cpp | 2 +- 12 files changed, 780 insertions(+), 435 deletions(-) create mode 100644 src/js_compat.hpp delete mode 100644 src/js_list.cpp create mode 100644 src/jsc/jsc_list.cpp create mode 100644 src/jsc/jsc_list.hpp create mode 100644 src/jsc/jsc_util.hpp diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 2110ecdf..3ff11c4f 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 029048201C0428DF00ABDED4 /* rpc.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048101C0428DF00ABDED4 /* rpc.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; 029048371C042A3C00ABDED4 /* platform.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048351C042A3C00ABDED4 /* platform.hpp */; }; 0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0290483A1C042EE200ABDED4 /* RealmJS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */; }; 02B58CCE1AE99D4D009B348C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; }; @@ -38,7 +39,6 @@ 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; }; F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; }; F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* js_init.cpp */; }; - F63FF2C71C12469E00B3B8E0 /* js_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048031C0428DF00ABDED4 /* js_list.cpp */; }; F63FF2C81C12469E00B3B8E0 /* js_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048051C0428DF00ABDED4 /* js_object.cpp */; }; F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; }; F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048091C0428DF00ABDED4 /* js_results.cpp */; }; @@ -106,7 +106,6 @@ 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ios/RealmJSTests.mm; sourceTree = ""; }; 029048011C0428DF00ABDED4 /* js_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_init.cpp; sourceTree = ""; }; 029048021C0428DF00ABDED4 /* js_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = js_init.h; sourceTree = ""; }; - 029048031C0428DF00ABDED4 /* js_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_list.cpp; sourceTree = ""; }; 029048041C0428DF00ABDED4 /* js_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_list.hpp; sourceTree = ""; }; 029048051C0428DF00ABDED4 /* js_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_object.cpp; sourceTree = ""; }; 029048061C0428DF00ABDED4 /* js_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_object.hpp; sourceTree = ""; }; @@ -124,6 +123,8 @@ 029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = ""; }; 0290483A1C042EE200ABDED4 /* RealmJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmJS.h; sourceTree = ""; }; 02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_list.cpp; path = jsc/jsc_list.cpp; sourceTree = ""; }; + 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_list.hpp; path = jsc/jsc_list.hpp; sourceTree = ""; }; 02B58CB11AE99CEC009B348C /* RealmJS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmJS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -247,7 +248,6 @@ 029048021C0428DF00ABDED4 /* js_init.h */, F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */, F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */, - 029048031C0428DF00ABDED4 /* js_list.cpp */, 029048041C0428DF00ABDED4 /* js_list.hpp */, 029048051C0428DF00ABDED4 /* js_object.cpp */, 029048061C0428DF00ABDED4 /* js_object.hpp */, @@ -259,6 +259,8 @@ 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, 0290480D1C0428DF00ABDED4 /* js_util.cpp */, 0290480E1C0428DF00ABDED4 /* js_util.hpp */, + 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, + 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, 0290480F1C0428DF00ABDED4 /* rpc.cpp */, 029048101C0428DF00ABDED4 /* rpc.hpp */, @@ -652,10 +654,10 @@ 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */, F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */, 02F59EC31C88F17D007F774C /* results.cpp in Sources */, + 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */, F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */, F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */, 02F59ECA1C88F190007F774C /* parser.cpp in Sources */, - F63FF2C71C12469E00B3B8E0 /* js_list.cpp in Sources */, F63FF2C81C12469E00B3B8E0 /* js_object.cpp in Sources */, 02F59EE11C88F2BB007F774C /* async_query.cpp in Sources */, 02F59EC01C88F17D007F774C /* list.cpp in Sources */, diff --git a/src/js_compat.hpp b/src/js_compat.hpp new file mode 100644 index 00000000..f13e177c --- /dev/null +++ b/src/js_compat.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "types.hpp" +#include + +namespace realm { +namespace js { + +void *GetInternal(Types::ObjectType jsObject) { + return JSObjectGetPrivate(jsObject); +} + + +std::string StringForStringType(Types::StringType jsString); +std::string StringForValueType(Types::ContextType ctx, Types::ValueType value); +std::string ValidatedStringForValueType(Types::ContextType ctx, Types::ValueType value, const char * name = nullptr); +bool ValidatedBooleanForValueType(Types::ContextType ctx, Types::ValueType value, const char * name = nullptr); + +Types::StringType StringTypeForString(const std::string &str); +Types::ValueType ValueTypeForString(Types::ContextType ctx, const std::string &str); + +bool IsValueTypeArray(Types::ContextType ctx, Types::ValueType value); +bool IsValueTypeArrayBuffer(Types::ContextType ctx, Types::ValueType value); +bool IsValueTypeDate(Types::ContextType ctx, Types::ValueType value); + +Types::ObjectType ValidatedValueTypeToObject(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); +Types::ObjectType ValidatedValueTypeToDate(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); +Types::ObjectType ValidatedValueTypeToFunction(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); +double ValidatedValueTypeToNumber(Types::ContextType ctx, Types::ValueType value); +Types::ValueType ValidatedPropertyValue(Types::ContextType ctx, Types::ObjectType object, Types::StringType property); +Types::ValueType ValidatedPropertyAtIndex(Types::ContextType ctx, Types::ObjectType object, unsigned int index); +Types::ObjectType ValidatedObjectProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property, const char *err = NULL); +Types::ObjectType ValidatedObjectAtIndex(Types::ContextType ctx, Types::ObjectType object, unsigned int index); +std::string ValidatedStringProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property); +bool ValidatedBooleanProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property, const char *err = NULL); +size_t ValidatedListLength(Types::ContextType ctx, Types::ObjectType object); +void ValidatedSetProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType propertyName, Types::ValueType value, JSPropertyAttributes attributes = 0); + +bool IsValueTypeIsObject(Types::ContextType ctx, Types::ValueType value); +bool IsValueTypeObjectOfType(Types::ContextType ctx, Types::ValueType value, Types::StringType type); +bool ObjectTypeHasProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType propName); + +template +void SetReturnNumber(Types::ContextType ctx, Types::ValueType &returnObject, T number); +void SetReturnArray(Types::ContextType ctx, size_t count, const Types::ValueType *objects, Types::ValueType &returnObject);\ +void SetReturnUndefined(Types::ContextType ctx, Types::ValueType &returnObject); + +void SetException(Types::ContextType ctx, Types::ValueType * &exceptionObject, std::exception &exception); + +}} \ No newline at end of file diff --git a/src/js_init.cpp b/src/js_init.cpp index 3c1623c4..e000f17c 100644 --- a/src/js_init.cpp +++ b/src/js_init.cpp @@ -20,7 +20,7 @@ #include "js_realm.hpp" #include "js_object.hpp" #include "js_collection.hpp" -#include "js_list.hpp" +#include "jsc_list.hpp" #include "js_results.hpp" #include "js_util.hpp" #include "js_schema.hpp" diff --git a/src/js_list.cpp b/src/js_list.cpp deleted file mode 100644 index 79eb1606..00000000 --- a/src/js_list.cpp +++ /dev/null @@ -1,300 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_list.hpp" -#include "js_collection.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "js_util.hpp" - -#include "object_accessor.hpp" -#include "parser.hpp" -#include "query_builder.hpp" - -#include - -using RJSAccessor = realm::NativeAccessor; -using namespace realm; - - -void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { - returnObject = JSValueMakeUndefined(ctx); -} - -template -void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { - returnObject = JSValueMakeNumber(ctx, number); -} - -void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { - returnObject = JSObjectMakeArray(ctx, count, objects, NULL); -} - -void RJSSetException(JSContextRef ctx, JSValueRef * &exceptionObject, std::exception &exception) { - if (exceptionObject) { - *exceptionObject = RJSMakeError(ctx, exception); - } -} - -JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - try { - List *list = RJSGetInternal(object); - std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr == "length") { - return JSValueMakeNumber(ctx, list->size()); - } - - return RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(RJSValidatedPositiveIndex(indexStr)))); - } - catch (std::out_of_range &exp) { - // getters for nonexistent properties in JS should always return undefined - return JSValueMakeUndefined(ctx); - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -bool ListSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { - try { - List *list = RJSGetInternal(object); - std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr == "length") { - throw std::runtime_error("The 'length' property is readonly."); - } - - list->set(ctx, value, RJSValidatedPositiveIndex(indexStr)); - return true; - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - return false; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return false; - } -} - -void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { - List *list = RJSGetInternal(object); - size_t size = list->size(); - - char str[32]; - for (size_t i = 0; i < size; i++) { - sprintf(str, "%zu", i); - JSStringRef name = JSStringCreateWithUTF8CString(str); - JSPropertyNameAccumulatorAddName(propertyNames, name); - JSStringRelease(name); - } -} - -template -void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { - list->add(ctx, arguments[i]); - } - RJSSetReturnNumber(ctx, returnObject, list->size()); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - - size_t size = list->size(); - if (size == 0) { - list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); - } - else { - size_t index = size - 1; - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); - list->remove(index); - } - } - catch (std::exception &exception) { - RJSSetException(ctx, exceptionObject, exception); - } -} - - -template -void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { - list->insert(ctx, arguments[i], i); - } - RJSSetReturnNumber(ctx, returnObject, list->size()); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - if (list->size() == 0) { - list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); - } - else { - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); - list->remove(0); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - size_t size = list->size(); - - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - long index = std::min(RJSValidatedValueToNumber(ctx, arguments[0]), size); - if (index < 0) { - index = std::max(size + index, 0); - } - - long remove; - if (argumentCount < 2) { - remove = size - index; - } - else { - remove = std::max(RJSValidatedValueToNumber(ctx, arguments[1]), 0); - remove = std::min(remove, size - index); - } - - std::vector removedObjects(remove); - for (size_t i = 0; i < remove; i++) { - removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); - list->remove(index); - } - for (size_t i = 2; i < argumentCount; i++) { - list->insert(ctx, arguments[i], index + i - 2); - } - RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - - -template -void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - List *list = RJSGetInternal(thisObject); - RJSValidateArgumentRange(argumentCount, 1, 2); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -#define LIST_METHOD(METHOD_NAME) \ -JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ - JSValueRef returnObject = NULL; \ - METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, jsException); \ - return returnObject; \ -} - -LIST_METHOD(ListPush) -LIST_METHOD(ListPop) -LIST_METHOD(ListUnshift) -LIST_METHOD(ListShift) -LIST_METHOD(ListSplice) -LIST_METHOD(ListStaticResults) -LIST_METHOD(ListFiltered) -LIST_METHOD(ListSorted) - -JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { - return RJSWrapObject(ctx, RJSListClass(), new List(list)); -} - -static const JSStaticFunction RJSListFuncs[] = { - {"push", ListPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"pop", ListPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"shift", ListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"unshift", ListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"splice", ListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"filtered", ListFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"sorted", ListSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"snapshot", ListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSListClass() { - static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass()); - return s_listClass; -} diff --git a/src/js_list.hpp b/src/js_list.hpp index 50fe6716..a952a623 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -18,9 +18,168 @@ #pragma once +#include "js_list.hpp" +#include "js_collection.hpp" +#include "js_object.hpp" +#include "js_results.hpp" #include "js_util.hpp" + #include "shared_realm.hpp" #include "list.hpp" +#include "object_accessor.hpp" +#include "parser.hpp" +#include "query_builder.hpp" -JSClassRef RJSListClass(); -JSObjectRef RJSListCreate(JSContextRef ctx, realm::List &list); +#include + +using RJSAccessor = realm::NativeAccessor; +using namespace realm; + +template +void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + list->add(ctx, arguments[i]); + } + RJSSetReturnNumber(ctx, returnObject, list->size()); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + + size_t size = list->size(); + if (size == 0) { + list->verify_in_transaction(); + RJSSetReturnUndefined(ctx, returnObject); + } + else { + size_t index = size - 1; + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + list->remove(index); + } + } + catch (std::exception &exception) { + RJSSetException(ctx, exceptionObject, exception); + } +} + + +template +void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + list->insert(ctx, arguments[i], i); + } + RJSSetReturnNumber(ctx, returnObject, list->size()); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + if (list->size() == 0) { + list->verify_in_transaction(); + RJSSetReturnUndefined(ctx, returnObject); + } + else { + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); + list->remove(0); + } + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + size_t size = list->size(); + + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + long index = std::min(RJSValidatedValueToNumber(ctx, arguments[0]), size); + if (index < 0) { + index = std::max(size + index, 0); + } + + long remove; + if (argumentCount < 2) { + remove = size - index; + } + else { + remove = std::max(RJSValidatedValueToNumber(ctx, arguments[1]), 0); + remove = std::min(remove, size - index); + } + + std::vector removedObjects(remove); + for (size_t i = 0; i < remove; i++) { + removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + list->remove(index); + } + for (size_t i = 2; i < argumentCount; i++) { + list->insert(ctx, arguments[i], index + i - 2); + } + RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + + +template +void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + List *list = RJSGetInternal(thisObject); + RJSValidateArgumentRange(argumentCount, 1, 2); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} \ No newline at end of file diff --git a/src/js_object.cpp b/src/js_object.cpp index 13654b61..718a5b1a 100644 --- a/src/js_object.cpp +++ b/src/js_object.cpp @@ -20,7 +20,7 @@ #include "js_object.hpp" #include "js_results.hpp" #include "js_schema.hpp" -#include "js_list.hpp" +#include "jsc_list.hpp" #include "js_realm.hpp" #include "object_store.hpp" @@ -93,8 +93,6 @@ JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { return jsObject; } -extern JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const ObjectSchema &object_schema, JSObjectRef array); - namespace realm { template<> bool RJSAccessor::dict_has_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) { diff --git a/src/js_realm.cpp b/src/js_realm.cpp index 0bca0d23..e2a2068d 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -19,7 +19,7 @@ #include "js_realm.hpp" #include "js_object.hpp" #include "js_results.hpp" -#include "js_list.hpp" +#include "jsc_list.hpp" #include "js_schema.hpp" #include "platform.hpp" @@ -27,6 +27,7 @@ #include "impl/realm_coordinator.hpp" #include "object_accessor.hpp" #include "binding_context.hpp" +#include "results.hpp" #include #include @@ -310,47 +311,21 @@ std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, return RJSValidatedStringForValue(ctx, value, "objectType"); } -JSValueRef RealmObjects(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmObjects(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); - return RJSResultsCreate(ctx, sharedRealm, className); + returnObject = RJSResultsCreate(ctx, sharedRealm, className); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const ObjectSchema &object_schema, JSObjectRef array) { - // copy to dictionary - if (object_schema.properties.size() != RJSValidatedListLength(ctx, array)) { - throw std::runtime_error("Array must contain values for all object properties"); - } - - JSValueRef exception = NULL; - JSObjectRef dict = JSObjectMake(ctx, NULL, NULL); - for (unsigned int i = 0; i < object_schema.properties.size(); i++) { - JSStringRef nameStr = JSStringCreateWithUTF8CString(object_schema.properties[i].name.c_str()); - JSValueRef value = JSObjectGetPropertyAtIndex(ctx, array, i, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSObjectSetProperty(ctx, dict, nameStr, value, kJSPropertyAttributeNone, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSStringRelease(nameStr); - } - return dict; -} - -JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmCreateObject(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentRange(argumentCount, 2, 3); SharedRealm sharedRealm = *RJSGetInternal(thisObject); @@ -359,8 +334,7 @@ JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef auto object_schema = schema->find(className); if (object_schema == schema->end()) { - *jsException = RJSMakeError(ctx, "Object type '" + className + "' not found in schema."); - return NULL; + throw std::runtime_error("Object type '" + className + "' not found in schema."); } JSObjectRef object = RJSValidatedValueToObject(ctx, arguments[1]); @@ -370,66 +344,63 @@ JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef bool update = false; if (argumentCount == 3) { - update = JSValueToBoolean(ctx, arguments[2]); + update = RJSValidatedValueToBoolean(ctx, arguments[2]); } - return RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmDelete(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmDelete(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 1); - - if (RJSIsValueArray(ctx, arguments[0]) || - JSValueIsObjectOfClass(ctx, arguments[0], RJSResultsClass()) || - JSValueIsObjectOfClass(ctx, arguments[0], RJSListClass())) - { - JSObjectRef array = RJSValidatedValueToObject(ctx, arguments[0]); - size_t length = RJSValidatedListLength(ctx, array); - for (long i = length-1; i >= 0; i--) { - JSValueRef object = RJSValidatedObjectAtIndex(ctx, array, (unsigned int)i); - RealmDelete(ctx, function, thisObject, 1, &object, jsException); - if (*jsException) { - return NULL; - } - } - return NULL; - } - - if (!JSValueIsObjectOfClass(ctx, arguments[0], RJSObjectClass())) { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } - - Object *object = RJSGetInternal(RJSValidatedValueToObject(ctx, arguments[0])); - + SharedRealm realm = *RJSGetInternal(thisObject); - if (!realm->is_in_transaction()) { throw std::runtime_error("Can only delete objects within a transaction."); } - - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); - - return NULL; + + JSObjectRef arg = RJSValidatedValueToObject(ctx, arguments[0]); + if (RJSValueIsObjectOfClass(ctx, arg, RJSObjectClass())) { + Object *object = RJSGetInternal(arg); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + } + else if (RJSIsValueArray(ctx, arg)) { + size_t length = RJSValidatedListLength(ctx, arg); + for (long i = length-1; i >= 0; i--) { + JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); + if (!JSValueIsObjectOfClass(ctx, jsObject, RJSObjectClass())) { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } + + Object *object = RJSGetInternal(jsObject); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + } + } + else if(RJSValueIsObjectOfClass(ctx, arg, RJSResultsClass())) { + Results *results = RJSGetInternal(arg); + results->clear(); + } + else if(RJSValueIsObjectOfClass(ctx, arg, RJSListClass())) { + List *list = RJSGetInternal(arg); + list->delete_all(); + } + else { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmDeleteAll(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmDeleteAll(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 0); SharedRealm realm = *RJSGetInternal(thisObject); @@ -441,39 +412,29 @@ JSValueRef RealmDeleteAll(JSContextRef ctx, JSObjectRef function, JSObjectRef th for (auto objectSchema : *realm->config().schema) { ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); } - return NULL; } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmWrite(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void RealmWrite(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { + SharedRealm realm = *RJSGetInternal(thisObject); try { RJSValidateArgumentCount(argumentCount, 1); JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]); - SharedRealm realm = *RJSGetInternal(thisObject); realm->begin_transaction(); - JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException); - if (*jsException) { - realm->cancel_transaction(); - } - else { - realm->commit_transaction(); - } + RJSCallFunction(ctx, object, thisObject, 0, NULL); + realm->commit_transaction(); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); + if (realm->is_in_transaction()) { + realm->cancel_transaction(); } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } - - return NULL; } std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { @@ -484,44 +445,36 @@ std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { return name; } -JSValueRef RealmAddListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmAddListener(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 2); __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); static_cast(realm->m_binding_context.get())->add_notification(callback); - return NULL; } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmRemoveListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmRemoveListener(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 2); __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); static_cast(realm->m_binding_context.get())->remove_notification(callback); - return NULL; } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmRemoveAllListeners(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmRemoveAllListeners(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentRange(argumentCount, 0, 1); if (argumentCount) { RJSValidatedNotificationName(ctx, arguments[0]); @@ -529,30 +482,33 @@ JSValueRef RealmRemoveAllListeners(JSContextRef ctx, JSObjectRef function, JSObj SharedRealm realm = *RJSGetInternal(thisObject); static_cast(realm->m_binding_context.get())->remove_all_notifications(); - return NULL; } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; + RJSSetException(ctx, exceptionObject, exp); } } -JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { +template +void RealmClose(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 0); SharedRealm realm = *RJSGetInternal(thisObject); realm->close(); } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } +WRAP_METHOD(RealmObjects) +WRAP_METHOD(RealmCreateObject) +WRAP_METHOD(RealmDelete) +WRAP_METHOD(RealmDeleteAll) +WRAP_METHOD(RealmWrite) +WRAP_METHOD(RealmAddListener) +WRAP_METHOD(RealmRemoveListener) +WRAP_METHOD(RealmRemoveAllListeners) +WRAP_METHOD(RealmClose) + static const JSStaticFunction RJSRealmFuncs[] = { {"objects", RealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"create", RealmCreateObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, diff --git a/src/js_util.hpp b/src/js_util.hpp index 0b219337..c55cba4b 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -31,6 +31,13 @@ #include "property.hpp" #include "schema.hpp" +#define WRAP_METHOD(METHOD_NAME) \ +JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ + JSValueRef returnObject = NULL; \ + METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, jsException); \ + return returnObject; \ +} + template inline void RJSFinalize(JSObjectRef object) { @@ -149,6 +156,13 @@ static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef valu return number; } +static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value) { + if (!JSValueIsBoolean(ctx, value)) { + throw std::invalid_argument("Value is not a boolean."); + } + return JSValueToBoolean(ctx, value); +} + static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { JSValueRef exception = NULL; JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); @@ -245,3 +259,58 @@ static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JS return ret; } + +static inline void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { + returnObject = JSValueMakeUndefined(ctx); +} + +template +static inline void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { + returnObject = JSValueMakeNumber(ctx, number); +} + +static inline void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { + returnObject = JSObjectMakeArray(ctx, count, objects, NULL); +} + +static inline void RJSSetException(JSContextRef ctx, JSValueRef * &exceptionObject, std::exception &exception) { + if (exceptionObject) { + *exceptionObject = RJSMakeError(ctx, exception); + } +} + +static JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const realm::ObjectSchema &object_schema, JSObjectRef array) { + // copy to dictionary + if (object_schema.properties.size() != RJSValidatedListLength(ctx, array)) { + throw std::runtime_error("Array must contain values for all object properties"); + } + + JSValueRef exception = NULL; + JSObjectRef dict = JSObjectMake(ctx, NULL, NULL); + for (unsigned int i = 0; i < object_schema.properties.size(); i++) { + JSStringRef nameStr = JSStringCreateWithUTF8CString(object_schema.properties[i].name.c_str()); + JSValueRef value = JSObjectGetPropertyAtIndex(ctx, array, i, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + JSObjectSetProperty(ctx, dict, nameStr, value, kJSPropertyAttributeNone, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + JSStringRelease(nameStr); + } + return dict; +} + +static void RJSCallFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef *arguments) { + JSValueRef exception = NULL; + JSObjectCallAsFunction(ctx, function, object, argumentCount, arguments, &exception); + if (exception) { + throw RJSException(ctx, exception); + } +} + + +static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) { + return JSValueIsObjectOfClass(ctx, value, jsClass); +} diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp new file mode 100644 index 00000000..653249c5 --- /dev/null +++ b/src/jsc/jsc_list.cpp @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "jsc_list.hpp" +#include "js_list.hpp" + +#include + +using RJSAccessor = realm::NativeAccessor; +using namespace realm; + +JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { + try { + List *list = RJSGetInternal(object); + std::string indexStr = RJSStringForJSString(propertyName); + if (indexStr == "length") { + return JSValueMakeNumber(ctx, list->size()); + } + + return RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(RJSValidatedPositiveIndex(indexStr)))); + } + catch (std::out_of_range &exp) { + // getters for nonexistent properties in JS should always return undefined + return JSValueMakeUndefined(ctx); + } + catch (std::invalid_argument &exp) { + // for stol failure this could be another property that is handled externally, so ignore + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +bool ListSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { + try { + List *list = RJSGetInternal(object); + std::string indexStr = RJSStringForJSString(propertyName); + if (indexStr == "length") { + throw std::runtime_error("The 'length' property is readonly."); + } + + list->set(ctx, value, RJSValidatedPositiveIndex(indexStr)); + return true; + } + catch (std::invalid_argument &exp) { + // for stol failure this could be another property that is handled externally, so ignore + return false; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return false; + } +} + +void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { + List *list = RJSGetInternal(object); + size_t size = list->size(); + + char str[32]; + for (size_t i = 0; i < size; i++) { + sprintf(str, "%zu", i); + JSStringRef name = JSStringCreateWithUTF8CString(str); + JSPropertyNameAccumulatorAddName(propertyNames, name); + JSStringRelease(name); + } +} + +WRAP_METHOD(ListPush) +WRAP_METHOD(ListPop) +WRAP_METHOD(ListUnshift) +WRAP_METHOD(ListShift) +WRAP_METHOD(ListSplice) +WRAP_METHOD(ListStaticResults) +WRAP_METHOD(ListFiltered) +WRAP_METHOD(ListSorted) + +JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { + return RJSWrapObject(ctx, RJSListClass(), new List(list)); +} + +static const JSStaticFunction RJSListFuncs[] = { + {"push", ListPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"pop", ListPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"shift", ListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"unshift", ListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"splice", ListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"filtered", ListFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"sorted", ListSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"snapshot", ListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + +JSClassRef RJSListClass() { + static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass()); + return s_listClass; +} diff --git a/src/jsc/jsc_list.hpp b/src/jsc/jsc_list.hpp new file mode 100644 index 00000000..e5592661 --- /dev/null +++ b/src/jsc/jsc_list.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "js_util.hpp" +#include "list.hpp" + +JSClassRef RJSListClass(); +JSObjectRef RJSListCreate(JSContextRef ctx, realm::List &list); diff --git a/src/jsc/jsc_util.hpp b/src/jsc/jsc_util.hpp new file mode 100644 index 00000000..ce9b370d --- /dev/null +++ b/src/jsc/jsc_util.hpp @@ -0,0 +1,251 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "property.hpp" +#include "schema.hpp" + +namespace realm { +namespace js { + +template +inline void RJSFinalize(JSObjectRef object) { + delete static_cast(JSObjectGetPrivate(object)); + JSObjectSetPrivate(object, NULL); +} + +template +inline JSObjectRef RJSWrapObject(JSContextRef ctx, JSClassRef jsClass, T object, JSValueRef prototype = NULL) { + JSObjectRef ref = JSObjectMake(ctx, jsClass, (void *)object); + if (prototype) { + JSObjectSetPrototype(ctx, ref, prototype); + } + return ref; +} + +template +inline T RJSGetInternal(JSObjectRef jsObject) { + return static_cast(JSObjectGetPrivate(jsObject)); +} + +template +JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL, + JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL) { + JSClassDefinition classDefinition = kJSClassDefinitionEmpty; + classDefinition.className = name; + classDefinition.finalize = RJSFinalize; + classDefinition.getProperty = getter; + classDefinition.setProperty = setter; + classDefinition.staticFunctions = funcs; + classDefinition.getPropertyNames = propertyNames; + classDefinition.parentClass = parentClass; + return JSClassCreate(&classDefinition); +} + +std::string RJSStringForJSString(JSStringRef jsString); +std::string RJSStringForValue(JSContextRef ctx, JSValueRef value); +std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name = nullptr); + +JSStringRef RJSStringForString(const std::string &str); +JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str); + +inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected, const char *message = NULL) { + if (argumentCount != expected) { + throw std::invalid_argument(message ?: "Invalid arguments"); + } +} + +inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected, const char *message = NULL) { + if (argumentCount < expected) { + throw std::invalid_argument(message ?: "Invalid arguments"); + } +} + +inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max, const char *message = NULL) { + if (argumentCount < min || argumentCount > max) { + throw std::invalid_argument(message ?: "Invalid arguments"); + } +} + +class RJSException : public std::runtime_error { +public: + RJSException(JSContextRef ctx, JSValueRef &ex) : std::runtime_error(RJSStringForValue(ctx, ex)), + m_jsException(ex) {} + JSValueRef exception() { return m_jsException; } + +private: + JSValueRef m_jsException; +}; + +JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp); +JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp); +JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message); + +bool RJSIsValueArray(JSContextRef ctx, JSValueRef value); +bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value); +bool RJSIsValueDate(JSContextRef ctx, JSValueRef value); + +static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = NULL) { + JSObjectRef object = JSValueToObject(ctx, value, NULL); + if (!object) { + throw std::runtime_error(message ?: "Value is not an object."); + } + return object; +} + +static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = NULL) { + JSObjectRef object = JSValueToObject(ctx, value, NULL); + if (!object || !RJSIsValueDate(ctx, object)) { + throw std::runtime_error(message ?: "Value is not a date."); + } + 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) { + if (JSValueIsNull(ctx, value)) { + throw std::invalid_argument("`null` is not a number."); + } + + JSValueRef exception = NULL; + double number = JSValueToNumber(ctx, value, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + if (isnan(number)) { + throw std::invalid_argument("Value not convertible to a number."); + } + return number; +} + +static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { + JSValueRef exception = NULL; + JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + return propertyValue; +} + +static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { + JSValueRef exception = NULL; + JSValueRef propertyValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + return propertyValue; +} + +static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) { + JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); + if (JSValueIsUndefined(ctx, propertyValue)) { + throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined"); + } + return RJSValidatedValueToObject(ctx, propertyValue, err); +} + +static inline JSObjectRef RJSValidatedObjectAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { + return RJSValidatedValueToObject(ctx, RJSValidatedPropertyAtIndex(ctx, object, index)); +} + +static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property) { + JSValueRef exception = NULL; + JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + return RJSValidatedStringForValue(ctx, propertyValue, RJSStringForJSString(property).c_str()); +} + +static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object) { + JSValueRef exception = NULL; + static JSStringRef lengthString = JSStringCreateWithUTF8CString("length"); + JSValueRef lengthValue = JSObjectGetProperty(ctx, object, lengthString, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + if (!JSValueIsNumber(ctx, lengthValue)) { + throw std::runtime_error("Missing property 'length'"); + } + + return RJSValidatedValueToNumber(ctx, lengthValue); +} + +static inline void RJSValidatedSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes = 0) { + JSValueRef exception = NULL; + JSObjectSetProperty(ctx, object, propertyName, value, attributes, &exception); + if (exception) { + throw RJSException(ctx, exception); + } +} + +template +T stot(const std::string s) { + std::istringstream iss(s); + T value; + iss >> value; + if (iss.fail()) { + throw std::invalid_argument("Cannot convert string '" + s + "'"); + } + return value; +} + +static inline size_t RJSValidatedPositiveIndex(std::string indexStr) { + long index = stot(indexStr); + if (index < 0) { + throw std::out_of_range(std::string("Index ") + indexStr + " cannot be less than zero."); + } + return index; +} + +static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JSStringRef type) { + JSObjectRef globalObject = JSContextGetGlobalObject(ctx); + + JSValueRef exception = NULL; + JSValueRef constructorValue = JSObjectGetProperty(ctx, globalObject, type, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + + bool ret = JSValueIsInstanceOfConstructor(ctx, value, RJSValidatedValueToObject(ctx, constructorValue), &exception); + if (exception) { + throw RJSException(ctx, exception); + } + + return ret; +} + +}} diff --git a/src/rpc.cpp b/src/rpc.cpp index d6d70fb6..e615eacf 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -24,7 +24,7 @@ #include "js_init.h" #include "js_object.hpp" #include "js_results.hpp" -#include "js_list.hpp" +#include "jsc_list.hpp" #include "js_realm.hpp" #include "js_util.hpp" From b4990fbbff3d3f6f4c7e74e87b7c6aedd76fb5b9 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 29 Mar 2016 13:36:01 -0700 Subject: [PATCH 03/48] convert more of Realm --- src/ios/RealmJS.xcodeproj/project.pbxproj | 2 + src/js_compat.hpp | 68 --- src/js_list.hpp | 32 +- src/js_realm.cpp | 67 +-- src/js_util.hpp | 4 +- src/jsc/js_compat.hpp | 34 ++ src/jsc/js_realm.cpp | 572 ++++++++++++++++++++++ src/jsc/js_realm.hpp | 36 ++ src/jsc/types.hpp | 37 ++ 9 files changed, 734 insertions(+), 118 deletions(-) delete mode 100644 src/js_compat.hpp create mode 100644 src/jsc/js_compat.hpp create mode 100644 src/jsc/js_realm.cpp create mode 100644 src/jsc/js_realm.hpp create mode 100644 src/jsc/types.hpp diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 3ff11c4f..3dd31948 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ 02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_list.cpp; path = jsc/jsc_list.cpp; sourceTree = ""; }; 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_list.hpp; path = jsc/jsc_list.hpp; sourceTree = ""; }; + 02AFE5B11CAB133500953DA3 /* js_compat.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = js_compat.hpp; path = jsc/js_compat.hpp; sourceTree = ""; }; 02B58CB11AE99CEC009B348C /* RealmJS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmJS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -259,6 +260,7 @@ 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, 0290480D1C0428DF00ABDED4 /* js_util.cpp */, 0290480E1C0428DF00ABDED4 /* js_util.hpp */, + 02AFE5B11CAB133500953DA3 /* js_compat.hpp */, 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, diff --git a/src/js_compat.hpp b/src/js_compat.hpp deleted file mode 100644 index f13e177c..00000000 --- a/src/js_compat.hpp +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "types.hpp" -#include - -namespace realm { -namespace js { - -void *GetInternal(Types::ObjectType jsObject) { - return JSObjectGetPrivate(jsObject); -} - - -std::string StringForStringType(Types::StringType jsString); -std::string StringForValueType(Types::ContextType ctx, Types::ValueType value); -std::string ValidatedStringForValueType(Types::ContextType ctx, Types::ValueType value, const char * name = nullptr); -bool ValidatedBooleanForValueType(Types::ContextType ctx, Types::ValueType value, const char * name = nullptr); - -Types::StringType StringTypeForString(const std::string &str); -Types::ValueType ValueTypeForString(Types::ContextType ctx, const std::string &str); - -bool IsValueTypeArray(Types::ContextType ctx, Types::ValueType value); -bool IsValueTypeArrayBuffer(Types::ContextType ctx, Types::ValueType value); -bool IsValueTypeDate(Types::ContextType ctx, Types::ValueType value); - -Types::ObjectType ValidatedValueTypeToObject(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); -Types::ObjectType ValidatedValueTypeToDate(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); -Types::ObjectType ValidatedValueTypeToFunction(Types::ContextType ctx, Types::ValueType value, const char *message = NULL); -double ValidatedValueTypeToNumber(Types::ContextType ctx, Types::ValueType value); -Types::ValueType ValidatedPropertyValue(Types::ContextType ctx, Types::ObjectType object, Types::StringType property); -Types::ValueType ValidatedPropertyAtIndex(Types::ContextType ctx, Types::ObjectType object, unsigned int index); -Types::ObjectType ValidatedObjectProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property, const char *err = NULL); -Types::ObjectType ValidatedObjectAtIndex(Types::ContextType ctx, Types::ObjectType object, unsigned int index); -std::string ValidatedStringProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property); -bool ValidatedBooleanProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType property, const char *err = NULL); -size_t ValidatedListLength(Types::ContextType ctx, Types::ObjectType object); -void ValidatedSetProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType propertyName, Types::ValueType value, JSPropertyAttributes attributes = 0); - -bool IsValueTypeIsObject(Types::ContextType ctx, Types::ValueType value); -bool IsValueTypeObjectOfType(Types::ContextType ctx, Types::ValueType value, Types::StringType type); -bool ObjectTypeHasProperty(Types::ContextType ctx, Types::ObjectType object, Types::StringType propName); - -template -void SetReturnNumber(Types::ContextType ctx, Types::ValueType &returnObject, T number); -void SetReturnArray(Types::ContextType ctx, size_t count, const Types::ValueType *objects, Types::ValueType &returnObject);\ -void SetReturnUndefined(Types::ContextType ctx, Types::ValueType &returnObject); - -void SetException(Types::ContextType ctx, Types::ValueType * &exceptionObject, std::exception &exception); - -}} \ No newline at end of file diff --git a/src/js_list.hpp b/src/js_list.hpp index a952a623..660bc981 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -35,8 +35,8 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm; -template -void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListPush(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); @@ -50,8 +50,8 @@ void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const } } -template -void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListPop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); @@ -73,8 +73,8 @@ void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const A } -template -void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListUnshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); @@ -88,8 +88,8 @@ void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, con } } -template -void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListShift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); @@ -107,8 +107,8 @@ void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const } } -template -void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListSplice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); size_t size = list->size(); @@ -144,8 +144,8 @@ void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, cons } -template -void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListStaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); @@ -156,8 +156,8 @@ void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCoun } } -template -void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListFiltered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); @@ -170,8 +170,8 @@ void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, co } } -template -void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void ListSorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { List *list = RJSGetInternal(thisObject); RJSValidateArgumentRange(argumentCount, 1, 2); diff --git a/src/js_realm.cpp b/src/js_realm.cpp index e2a2068d..a6689ef9 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -238,34 +238,34 @@ static const JSStaticValue RealmStaticProperties[] = { {NULL, NULL} }; -JSValueRef RealmSchemaVersion(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { +template +void RealmSchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentRange(argumentCount, 1, 2); Realm::Config config; config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); if (argumentCount == 2) { - JSValueRef encryptionKeyValue = arguments[1]; + auto encryptionKeyValue = arguments[1]; std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); } auto version = Realm::get_schema_version(config); if (version == ObjectStore::NotVersioned) { - return JSValueMakeNumber(ctx, -1); + RJSSetReturnNumber(ctx, returnObject, -1); } else { - return JSValueMakeNumber(ctx, version); + RJSSetReturnNumber(ctx, returnObject, version); } } catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } + RJSSetException(ctx, exceptionObject, exp); } - return NULL; } +WRAP_METHOD(RealmSchemaVersion) + static const JSStaticFunction RealmConstructorFuncs[] = { {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL}, @@ -311,8 +311,8 @@ std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, return RJSValidatedStringForValue(ctx, value, "objectType"); } -template -void RealmObjects(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmObjects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); @@ -324,8 +324,9 @@ void RealmObjects(ContextType ctx, ThisType thisObject, size_t argumentCount, co } } -template -void RealmCreateObject(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmCreateObject(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { RJSValidateArgumentRange(argumentCount, 2, 3); SharedRealm sharedRealm = *RJSGetInternal(thisObject); @@ -337,7 +338,7 @@ void RealmCreateObject(ContextType ctx, ThisType thisObject, size_t argumentCoun throw std::runtime_error("Object type '" + className + "' not found in schema."); } - JSObjectRef object = RJSValidatedValueToObject(ctx, arguments[1]); + auto object = RJSValidatedValueToObject(ctx, arguments[1]); if (RJSIsValueArray(ctx, arguments[1])) { object = RJSDictForPropertyArray(ctx, *object_schema, object); } @@ -347,15 +348,15 @@ void RealmCreateObject(ContextType ctx, ThisType thisObject, size_t argumentCoun update = RJSValidatedValueToBoolean(ctx, arguments[2]); } - returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); } } -template -void RealmDelete(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmDelete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 1); SharedRealm realm = *RJSGetInternal(thisObject); @@ -363,7 +364,7 @@ void RealmDelete(ContextType ctx, ThisType thisObject, size_t argumentCount, con throw std::runtime_error("Can only delete objects within a transaction."); } - JSObjectRef arg = RJSValidatedValueToObject(ctx, arguments[0]); + auto arg = RJSValidatedValueToObject(ctx, arguments[0]); if (RJSValueIsObjectOfClass(ctx, arg, RJSObjectClass())) { Object *object = RJSGetInternal(arg); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); @@ -373,7 +374,7 @@ void RealmDelete(ContextType ctx, ThisType thisObject, size_t argumentCount, con size_t length = RJSValidatedListLength(ctx, arg); for (long i = length-1; i >= 0; i--) { JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); - if (!JSValueIsObjectOfClass(ctx, jsObject, RJSObjectClass())) { + if (!RJSValueIsObjectOfClass(ctx, jsObject, RJSObjectClass())) { throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); } @@ -399,8 +400,8 @@ void RealmDelete(ContextType ctx, ThisType thisObject, size_t argumentCount, con } } -template -void RealmDeleteAll(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmDeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 0); SharedRealm realm = *RJSGetInternal(thisObject); @@ -418,13 +419,13 @@ void RealmDeleteAll(ContextType ctx, ThisType thisObject, size_t argumentCount, } } -template -void RealmWrite(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void RealmWrite(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { SharedRealm realm = *RJSGetInternal(thisObject); try { RJSValidateArgumentCount(argumentCount, 1); - JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]); + auto object = RJSValidatedValueToFunction(ctx, arguments[0]); realm->begin_transaction(); RJSCallFunction(ctx, object, thisObject, 0, NULL); realm->commit_transaction(); @@ -445,11 +446,11 @@ std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { return name; } -template -void RealmAddListener(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmAddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 2); __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); static_cast(realm->m_binding_context.get())->add_notification(callback); @@ -459,11 +460,11 @@ void RealmAddListener(ContextType ctx, ThisType thisObject, size_t argumentCount } } -template -void RealmRemoveListener(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmRemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 2); __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); static_cast(realm->m_binding_context.get())->remove_notification(callback); @@ -473,8 +474,8 @@ void RealmRemoveListener(ContextType ctx, ThisType thisObject, size_t argumentCo } } -template -void RealmRemoveAllListeners(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmRemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentRange(argumentCount, 0, 1); if (argumentCount) { RJSValidatedNotificationName(ctx, arguments[0]); @@ -488,8 +489,8 @@ void RealmRemoveAllListeners(ContextType ctx, ThisType thisObject, size_t argume } } -template -void RealmClose(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try { +template +void RealmClose(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { RJSValidateArgumentCount(argumentCount, 0); SharedRealm realm = *RJSGetInternal(thisObject); realm->close(); diff --git a/src/js_util.hpp b/src/js_util.hpp index c55cba4b..c56e3fc3 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -29,6 +29,8 @@ #include #include #include "property.hpp" + +#include "js_compat.hpp" #include "schema.hpp" #define WRAP_METHOD(METHOD_NAME) \ @@ -38,7 +40,6 @@ JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisO return returnObject; \ } - template inline void RJSFinalize(JSObjectRef object) { delete static_cast(JSObjectGetPrivate(object)); @@ -314,3 +315,4 @@ static void RJSCallFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) { return JSValueIsObjectOfClass(ctx, value, jsClass); } + diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp new file mode 100644 index 00000000..9218484a --- /dev/null +++ b/src/jsc/js_compat.hpp @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "types.hpp" +#include + +namespace realm { +namespace js { + +static bool ValueIsUndefined(Types::Context ctx, Types::Value value) { return JSValueIsUndefined(ctx, value); } +static bool ValueIsNull(Types::Context ctx, Types::Value value) { return JSValueIsNull(ctx, value); } +static bool ValueIsBoolean(Types::Context ctx, Types::Value value) { return JSValueIsBoolean(ctx, value); } +static bool ValueIsNumber(Types::Context ctx, Types::Value value) { return JSValueIsNumber(ctx, value); } +static bool ValueIsString(Types::Context ctx, Types::Value value) { return JSValueIsString(ctx, value); } +static bool ValueIsObject(Types::Context ctx, Types::Value value) { return JSValueIsObject(ctx, value); } + +}} \ No newline at end of file diff --git a/src/jsc/js_realm.cpp b/src/jsc/js_realm.cpp new file mode 100644 index 00000000..5929c435 --- /dev/null +++ b/src/jsc/js_realm.cpp @@ -0,0 +1,572 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "js_realm.hpp" +#include "js_object.hpp" +#include "js_results.hpp" +#include "jsc_list.hpp" +#include "js_schema.hpp" +#include "platform.hpp" + +#include "shared_realm.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_accessor.hpp" +#include "binding_context.hpp" + +#include +#include + +using namespace realm; +using RJSAccessor = realm::NativeAccessor; + +class RJSRealmDelegate : public BindingContext { +public: + virtual void changes_available() { + assert(0); + } + virtual void did_change(std::vector const& observers, std::vector const& invalidated) { + notify("change"); + } + virtual std::vector get_observed_rows() { + return std::vector(); + } + virtual void will_change(std::vector const& observers, + std::vector const& invalidated) {} + + RJSRealmDelegate(WeakRealm realm, JSGlobalContextRef ctx) : m_context(ctx), m_realm(realm) { + JSGlobalContextRetain(m_context); + } + + ~RJSRealmDelegate() { + remove_all_notifications(); + + for (auto constructor : m_constructors) { + JSValueUnprotect(m_context, constructor.second); + } + for (auto objectDefaults : m_defaults) { + for (auto value : objectDefaults.second) { + JSValueUnprotect(m_context, value.second); + } + } + JSGlobalContextRelease(m_context); + } + + void add_notification(JSObjectRef notification) { + if (!m_notifications.count(notification)) { + JSValueProtect(m_context, notification); + m_notifications.insert(notification); + } + } + void remove_notification(JSObjectRef notification) { + if (m_notifications.count(notification)) { + JSValueUnprotect(m_context, notification); + m_notifications.erase(notification); + } + } + void remove_all_notifications() { + for (auto notification : m_notifications) { + JSValueUnprotect(m_context, notification); + } + m_notifications.clear(); + } + + std::map m_defaults; + std::map m_constructors; + + private: + std::set m_notifications; + JSGlobalContextRef m_context; + WeakRealm m_realm; + + void notify(const char *notification_name) { + JSValueRef arguments[2]; + SharedRealm realm = m_realm.lock(); + if (!realm) { + throw std::runtime_error("Realm no longer exists"); + } + JSObjectRef realm_object = RJSWrapObject(m_context, RJSRealmClass(), new SharedRealm(realm)); + arguments[0] = realm_object; + arguments[1] = RJSValueForString(m_context, notification_name); + + for (auto callback : m_notifications) { + JSValueRef ex = NULL; + JSObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, &ex); + if (ex) { + throw RJSException(m_context, ex); + } + } + } +}; + +std::map &RJSDefaults(Realm *realm) { + return static_cast(realm->m_binding_context.get())->m_defaults; +} + +std::map &RJSConstructors(Realm *realm) { + return static_cast(realm->m_binding_context.get())->m_constructors; +} + +// static std::string s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; +static std::string s_defaultPath = ""; +std::string RJSDefaultPath() { + if (s_defaultPath.size() == 0) { + s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; + } + return s_defaultPath; +} +void RJSSetDefaultPath(std::string path) { + s_defaultPath = path; +} + +static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { + return RJSValueForString(ctx, s_defaultPath); +} + +static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { + try { + s_defaultPath = RJSValidatedStringForValue(ctx, value, "defaultPath"); + } + catch (std::exception &ex) { + if (jsException) { + *jsException = RJSMakeError(ctx, ex); + } + } + return true; +} + +inline std::string RJSNormalizePath(std::string path) { + if (path.size() && path[0] != '/') { + return default_realm_file_directory() + "/" + path; + } + return path; +} + +JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + Realm::Config config; + std::map defaults; + std::map constructors; + if (argumentCount == 0) { + config.path = RJSDefaultPath(); + } + else if (argumentCount == 1) { + JSValueRef value = arguments[0]; + if (JSValueIsString(ctx, value)) { + config.path = RJSValidatedStringForValue(ctx, value, "path"); + } + else if (JSValueIsObject(ctx, value)) { + JSObjectRef object = RJSValidatedValueToObject(ctx, value); + + static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); + JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); + if (!JSValueIsUndefined(ctx, pathValue)) { + config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); + } + else { + config.path = RJSDefaultPath(); + } + + static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); + JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); + if (!JSValueIsUndefined(ctx, schemaValue)) { + config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); + } + + static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); + JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); + if (JSValueIsNumber(ctx, versionValue)) { + config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); + } + else { + config.schema_version = 0; + } + + static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); + JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); + if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; + } + } + } + else { + *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); + return NULL; + } + + config.path = RJSNormalizePath(config.path); + + ensure_directory_exists_for_file(config.path); + SharedRealm realm = Realm::get_shared_realm(config); + if (!realm->m_binding_context) { + realm->m_binding_context.reset(new RJSRealmDelegate(realm, JSContextGetGlobalContext(ctx))); + } + RJSDefaults(realm.get()) = defaults; + RJSConstructors(realm.get()) = constructors; + return RJSWrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); + } + catch (std::exception &ex) { + if (jsException) { + *jsException = RJSMakeError(ctx, ex); + } + return NULL; + } +} + +bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { + return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); +} + +static const JSStaticValue RealmStaticProperties[] = { + {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL} +}; + +JSValueRef RealmSchemaVersion(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentRange(argumentCount, 1, 2); + + Realm::Config config; + config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); + if (argumentCount == 2) { + JSValueRef encryptionKeyValue = arguments[1]; + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); + } + + auto version = Realm::get_schema_version(config); + if (version == ObjectStore::NotVersioned) { + return JSValueMakeNumber(ctx, -1); + } + else { + return JSValueMakeNumber(ctx, version); + } + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +static const JSStaticFunction RealmConstructorFuncs[] = { + {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + +JSClassRef RJSRealmConstructorClass() { + JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; + realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; + realmConstructorDefinition.className = "RealmConstructor"; + realmConstructorDefinition.callAsConstructor = RealmConstructor; + realmConstructorDefinition.hasInstance = RealmHasInstance; + realmConstructorDefinition.staticValues = RealmStaticProperties; + realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; + return JSClassCreate(&realmConstructorDefinition); +} + +JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { + static JSStringRef s_pathString = JSStringCreateWithUTF8CString("path"); + if (JSStringIsEqual(propertyName, s_pathString)) { + return RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); + } + + static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion"); + if (JSStringIsEqual(propertyName, s_schemaVersion)) { + return JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); + } + return NULL; +} + +std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { + if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { + JSObjectRef constructor = (JSObjectRef)value; + + for (auto pair : RJSConstructors(realm.get())) { + if (pair.second == constructor) { + return pair.first; + } + } + + throw std::runtime_error("Constructor was not registered in the schema for this Realm"); + } + + return RJSValidatedStringForValue(ctx, value, "objectType"); +} + +JSValueRef RealmObjects(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 1); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + return RJSResultsCreate(ctx, sharedRealm, className); + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const ObjectSchema &object_schema, JSObjectRef array) { + // copy to dictionary + if (object_schema.properties.size() != RJSValidatedListLength(ctx, array)) { + throw std::runtime_error("Array must contain values for all object properties"); + } + + JSValueRef exception = NULL; + JSObjectRef dict = JSObjectMake(ctx, NULL, NULL); + for (unsigned int i = 0; i < object_schema.properties.size(); i++) { + JSStringRef nameStr = JSStringCreateWithUTF8CString(object_schema.properties[i].name.c_str()); + JSValueRef value = JSObjectGetPropertyAtIndex(ctx, array, i, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + JSObjectSetProperty(ctx, dict, nameStr, value, kJSPropertyAttributeNone, &exception); + if (exception) { + throw RJSException(ctx, exception); + } + JSStringRelease(nameStr); + } + return dict; +} + +JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentRange(argumentCount, 2, 3); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + auto &schema = sharedRealm->config().schema; + auto object_schema = schema->find(className); + + if (object_schema == schema->end()) { + *jsException = RJSMakeError(ctx, "Object type '" + className + "' not found in schema."); + return NULL; + } + + JSObjectRef object = RJSValidatedValueToObject(ctx, arguments[1]); + if (RJSIsValueArray(ctx, arguments[1])) { + object = RJSDictForPropertyArray(ctx, *object_schema, object); + } + + bool update = false; + if (argumentCount == 3) { + update = JSValueToBoolean(ctx, arguments[2]); + } + + return RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmDelete(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 1); + + if (RJSIsValueArray(ctx, arguments[0]) || + JSValueIsObjectOfClass(ctx, arguments[0], RJSResultsClass()) || + JSValueIsObjectOfClass(ctx, arguments[0], RJSListClass())) + { + JSObjectRef array = RJSValidatedValueToObject(ctx, arguments[0]); + size_t length = RJSValidatedListLength(ctx, array); + for (long i = length-1; i >= 0; i--) { + JSValueRef object = RJSValidatedObjectAtIndex(ctx, array, (unsigned int)i); + RealmDelete(ctx, function, thisObject, 1, &object, jsException); + if (*jsException) { + return NULL; + } + } + return NULL; + } + + if (!JSValueIsObjectOfClass(ctx, arguments[0], RJSObjectClass())) { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } + + Object *object = RJSGetInternal(RJSValidatedValueToObject(ctx, arguments[0])); + + SharedRealm realm = *RJSGetInternal(thisObject); + + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } + + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmDeleteAll(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 0); + + SharedRealm realm = *RJSGetInternal(thisObject); + + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } + + for (auto objectSchema : *realm->config().schema) { + ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); + } + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmWrite(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 1); + + JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]); + SharedRealm realm = *RJSGetInternal(thisObject); + realm->begin_transaction(); + JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException); + if (*jsException) { + realm->cancel_transaction(); + } + else { + realm->commit_transaction(); + } + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } + + return NULL; +} + +std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { + std::string name = RJSValidatedStringForValue(ctx, value); + if (name != "change") { + throw std::runtime_error("Only the 'change' notification name is supported."); + } + return name; +} + +JSValueRef RealmAddListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); + JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast(realm->m_binding_context.get())->add_notification(callback); + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmRemoveListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); + JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast(realm->m_binding_context.get())->remove_notification(callback); + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmRemoveAllListeners(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentRange(argumentCount, 0, 1); + if (argumentCount) { + RJSValidatedNotificationName(ctx, arguments[0]); + } + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast(realm->m_binding_context.get())->remove_all_notifications(); + return NULL; + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + return NULL; + } +} + +JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + RJSValidateArgumentCount(argumentCount, 0); + SharedRealm realm = *RJSGetInternal(thisObject); + realm->close(); + } + catch (std::exception &exp) { + if (jsException) { + *jsException = RJSMakeError(ctx, exp); + } + } + return NULL; +} + +static const JSStaticFunction RJSRealmFuncs[] = { + {"objects", RealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"create", RealmCreateObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"delete", RealmDelete, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"deleteAll", RealmDeleteAll, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"write", RealmWrite, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"addListener", RealmAddListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"removeListener", RealmRemoveListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"removeAllListeners", RealmRemoveAllListeners, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"close", RealmClose, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + +JSClassRef RJSRealmClass() { + static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); + return s_realmClass; +} diff --git a/src/jsc/js_realm.hpp b/src/jsc/js_realm.hpp new file mode 100644 index 00000000..167e64bd --- /dev/null +++ b/src/jsc/js_realm.hpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "js_util.hpp" +#include + +namespace realm { + class Realm; + using ObjectDefaults = std::map; +} + +JSClassRef RJSRealmClass(); +JSClassRef RJSRealmConstructorClass(); + +std::string RJSDefaultPath(); +void RJSSetDefaultPath(std::string path); + +std::map &RJSDefaults(realm::Realm *realm); +std::map &RJSConstructors(realm::Realm *realm); diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp new file mode 100644 index 00000000..1d2c5406 --- /dev/null +++ b/src/jsc/types.hpp @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +namespace realm { +namespace js { + +struct Types { + using Context = JSContextRef; + using Value = JSValueRef; + using Object = JSObjectRef; + using String = JSStringRef; + using Function = JSObjectRef; + using Return = JSValueRef; +}; + +}} \ No newline at end of file From e7d954a72767c1b6e9fa13c5b328a8f83256b4b9 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 29 Mar 2016 14:12:27 -0700 Subject: [PATCH 04/48] wrap all types in a single type and use static class methods --- src/js_list.hpp | 75 +++++++++++++++++++++++++++---------------- src/js_util.hpp | 15 ++++++--- src/jsc/js_compat.hpp | 12 +++---- src/jsc/jsc_list.cpp | 33 ++++++++++--------- src/jsc/types.hpp | 3 +- 5 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index 660bc981..76a53dd7 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -18,7 +18,6 @@ #pragma once -#include "js_list.hpp" #include "js_collection.hpp" #include "js_object.hpp" #include "js_results.hpp" @@ -33,12 +32,31 @@ #include using RJSAccessor = realm::NativeAccessor; -using namespace realm; +namespace realm { +namespace js { -template -void ListPush(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +struct List { + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using ReturnType = typename T::Return; + using ExceptionType = typename T::Exception; + + static void Push(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Pop(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Unshift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Shift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Splice(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void StaticResults(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Filtered(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Sorted(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); +}; + +template +void List::Push(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); for (size_t i = 0; i < argumentCount; i++) { list->add(ctx, arguments[i]); @@ -50,10 +68,10 @@ void ListPush(ContextType ctx, ObjectType thisObject, size_t argumentCount, cons } } -template -void ListPop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Pop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); size_t size = list->size(); @@ -73,10 +91,10 @@ void ListPop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const } -template -void ListUnshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Unshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); for (size_t i = 0; i < argumentCount; i++) { list->insert(ctx, arguments[i], i); @@ -88,10 +106,10 @@ void ListUnshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, c } } -template -void ListShift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Shift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); if (list->size() == 0) { list->verify_in_transaction(); @@ -107,10 +125,10 @@ void ListShift(ContextType ctx, ObjectType thisObject, size_t argumentCount, con } } -template -void ListSplice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Splice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); size_t size = list->size(); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); @@ -144,10 +162,10 @@ void ListSplice(ContextType ctx, ObjectType thisObject, size_t argumentCount, co } -template -void ListStaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::StaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCount(argumentCount, 0); returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); } @@ -156,10 +174,10 @@ void ListStaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCo } } -template -void ListFiltered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Filtered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentCountIsAtLeast(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); @@ -170,10 +188,10 @@ void ListFiltered(ContextType ctx, ObjectType thisObject, size_t argumentCount, } } -template -void ListSorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { +template +void List::Sorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - List *list = RJSGetInternal(thisObject); + realm::List *list = RJSGetInternal(thisObject); RJSValidateArgumentRange(argumentCount, 1, 2); SharedRealm sharedRealm = *RJSGetInternal(thisObject); @@ -182,4 +200,7 @@ void ListSorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, co catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); } -} \ No newline at end of file +} + +} +} diff --git a/src/js_util.hpp b/src/js_util.hpp index c56e3fc3..72c891c6 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -33,10 +33,17 @@ #include "js_compat.hpp" #include "schema.hpp" +#define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ + JSValueRef returnObject = NULL; \ + CLASS_NAME::METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, *jsException); \ + return returnObject; \ +} + #define WRAP_METHOD(METHOD_NAME) \ JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ JSValueRef returnObject = NULL; \ - METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, jsException); \ + METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, *jsException); \ return returnObject; \ } @@ -274,10 +281,8 @@ static inline void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSVal returnObject = JSObjectMakeArray(ctx, count, objects, NULL); } -static inline void RJSSetException(JSContextRef ctx, JSValueRef * &exceptionObject, std::exception &exception) { - if (exceptionObject) { - *exceptionObject = RJSMakeError(ctx, exception); - } +static inline void RJSSetException(JSContextRef ctx, JSValueRef &exceptionObject, std::exception &exception) { + exceptionObject = RJSMakeError(ctx, exception); } static JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const realm::ObjectSchema &object_schema, JSObjectRef array) { diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index 9218484a..8e36cd85 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -24,11 +24,11 @@ namespace realm { namespace js { -static bool ValueIsUndefined(Types::Context ctx, Types::Value value) { return JSValueIsUndefined(ctx, value); } -static bool ValueIsNull(Types::Context ctx, Types::Value value) { return JSValueIsNull(ctx, value); } -static bool ValueIsBoolean(Types::Context ctx, Types::Value value) { return JSValueIsBoolean(ctx, value); } -static bool ValueIsNumber(Types::Context ctx, Types::Value value) { return JSValueIsNumber(ctx, value); } -static bool ValueIsString(Types::Context ctx, Types::Value value) { return JSValueIsString(ctx, value); } -static bool ValueIsObject(Types::Context ctx, Types::Value value) { return JSValueIsObject(ctx, value); } +static bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } +static bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } +static bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } +static bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNumber(ctx, value); } +static bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } +static bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } }} \ No newline at end of file diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index 653249c5..dc059cd6 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -86,28 +86,29 @@ void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccum } } -WRAP_METHOD(ListPush) -WRAP_METHOD(ListPop) -WRAP_METHOD(ListUnshift) -WRAP_METHOD(ListShift) -WRAP_METHOD(ListSplice) -WRAP_METHOD(ListStaticResults) -WRAP_METHOD(ListFiltered) -WRAP_METHOD(ListSorted) +using RJSList = realm::js::List; +WRAP_CLASS_METHOD(RJSList, Push) +WRAP_CLASS_METHOD(RJSList, Pop) +WRAP_CLASS_METHOD(RJSList, Unshift) +WRAP_CLASS_METHOD(RJSList, Shift) +WRAP_CLASS_METHOD(RJSList, Splice) +WRAP_CLASS_METHOD(RJSList, StaticResults) +WRAP_CLASS_METHOD(RJSList, Filtered) +WRAP_CLASS_METHOD(RJSList, Sorted) JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { return RJSWrapObject(ctx, RJSListClass(), new List(list)); } static const JSStaticFunction RJSListFuncs[] = { - {"push", ListPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"pop", ListPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"shift", ListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"unshift", ListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"splice", ListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"filtered", ListFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"sorted", ListSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"snapshot", ListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"push", RJSListPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"pop", RJSListPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"shift", RJSListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"unshift", RJSListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"splice", RJSListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"filtered", RJSListFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"sorted", RJSListSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"snapshot", RJSListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL}, }; diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp index 1d2c5406..b5f8db3d 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/types.hpp @@ -23,7 +23,7 @@ #include namespace realm { -namespace js { +namespace jsc { struct Types { using Context = JSContextRef; @@ -32,6 +32,7 @@ struct Types { using String = JSStringRef; using Function = JSObjectRef; using Return = JSValueRef; + using Exception = JSValueRef; }; }} \ No newline at end of file From 5b2a59d5c6a5f71103dc1c1532eb8c77c61fb67c Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 29 Mar 2016 16:17:57 -0700 Subject: [PATCH 05/48] converted BindingContext --- src/ios/RealmJS.xcodeproj/project.pbxproj | 2 + src/js_object.cpp | 9 +- src/js_realm.cpp | 114 ++++------------------ src/js_realm.hpp | 96 +++++++++++++++++- src/js_results.cpp | 16 ++- src/js_schema.cpp | 2 +- src/js_util.hpp | 9 -- src/jsc/js_compat.hpp | 27 ++++- src/jsc/jsc_list.cpp | 8 +- src/jsc/types.hpp | 2 + 10 files changed, 163 insertions(+), 122 deletions(-) diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 3dd31948..b12a7d69 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -100,6 +100,7 @@ /* Begin PBXFileReference section */ 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = ""; }; + 025678951CAB392000FB8501 /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = jsc/types.hpp; sourceTree = ""; }; 0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = ""; }; 0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = ""; }; @@ -261,6 +262,7 @@ 0290480D1C0428DF00ABDED4 /* js_util.cpp */, 0290480E1C0428DF00ABDED4 /* js_util.hpp */, 02AFE5B11CAB133500953DA3 /* js_compat.hpp */, + 025678951CAB392000FB8501 /* types.hpp */, 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, diff --git a/src/js_object.cpp b/src/js_object.cpp index 718a5b1a..505a2505 100644 --- a/src/js_object.cpp +++ b/src/js_object.cpp @@ -74,9 +74,10 @@ JSClassRef RJSObjectClass() { JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); - JSObjectRef constructor = RJSConstructors(object.realm().get())[object.get_object_schema().name]; + auto delegate = realm::js::get_delegate(object.realm().get()); + JSObjectRef constructor = delegate->m_constructors[object.get_object_schema().name]; JSObjectRef prototype = constructor ? RJSValidatedObjectProperty(ctx, constructor, prototypeString) : NULL; - JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype); + JSObjectRef jsObject = realm::js::WrapObject(ctx, RJSObjectClass(), new Object(object), prototype); if (constructor) { JSValueRef exception = NULL; @@ -117,12 +118,12 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR } template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name]; + auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; return defaults.find(prop_name) != defaults.end(); } template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name]; + auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; return defaults[prop_name]; } diff --git a/src/js_realm.cpp b/src/js_realm.cpp index a6689ef9..a9ce6f2f 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -29,99 +29,12 @@ #include "binding_context.hpp" #include "results.hpp" -#include #include + using namespace realm; using RJSAccessor = realm::NativeAccessor; -class RJSRealmDelegate : public BindingContext { -public: - virtual void changes_available() { - assert(0); - } - virtual void did_change(std::vector const& observers, std::vector const& invalidated) { - notify("change"); - } - virtual std::vector get_observed_rows() { - return std::vector(); - } - virtual void will_change(std::vector const& observers, - std::vector const& invalidated) {} - - RJSRealmDelegate(WeakRealm realm, JSGlobalContextRef ctx) : m_context(ctx), m_realm(realm) { - JSGlobalContextRetain(m_context); - } - - ~RJSRealmDelegate() { - remove_all_notifications(); - - for (auto constructor : m_constructors) { - JSValueUnprotect(m_context, constructor.second); - } - for (auto objectDefaults : m_defaults) { - for (auto value : objectDefaults.second) { - JSValueUnprotect(m_context, value.second); - } - } - JSGlobalContextRelease(m_context); - } - - void add_notification(JSObjectRef notification) { - if (!m_notifications.count(notification)) { - JSValueProtect(m_context, notification); - m_notifications.insert(notification); - } - } - void remove_notification(JSObjectRef notification) { - if (m_notifications.count(notification)) { - JSValueUnprotect(m_context, notification); - m_notifications.erase(notification); - } - } - void remove_all_notifications() { - for (auto notification : m_notifications) { - JSValueUnprotect(m_context, notification); - } - m_notifications.clear(); - } - - std::map m_defaults; - std::map m_constructors; - - private: - std::set m_notifications; - JSGlobalContextRef m_context; - WeakRealm m_realm; - - void notify(const char *notification_name) { - JSValueRef arguments[2]; - SharedRealm realm = m_realm.lock(); - if (!realm) { - throw std::runtime_error("Realm no longer exists"); - } - JSObjectRef realm_object = RJSWrapObject(m_context, RJSRealmClass(), new SharedRealm(realm)); - arguments[0] = realm_object; - arguments[1] = RJSValueForString(m_context, notification_name); - - for (auto callback : m_notifications) { - JSValueRef ex = NULL; - JSObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, &ex); - if (ex) { - throw RJSException(m_context, ex); - } - } - } -}; - -std::map &RJSDefaults(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_defaults; -} - -std::map &RJSConstructors(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_constructors; -} - // static std::string s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; static std::string s_defaultPath = ""; std::string RJSDefaultPath() { @@ -214,12 +127,13 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a ensure_directory_exists_for_file(config.path); SharedRealm realm = Realm::get_shared_realm(config); + auto delegate = new js::RealmDelegate(realm, JSContextGetGlobalContext(ctx)); if (!realm->m_binding_context) { - realm->m_binding_context.reset(new RJSRealmDelegate(realm, JSContextGetGlobalContext(ctx))); + realm->m_binding_context.reset(delegate); } - RJSDefaults(realm.get()) = defaults; - RJSConstructors(realm.get()) = constructors; - return RJSWrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); + delegate->m_defaults = defaults; + delegate->m_constructors = constructors; + return js::WrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); } catch (std::exception &ex) { if (jsException) { @@ -299,7 +213,8 @@ std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { JSObjectRef constructor = (JSObjectRef)value; - for (auto pair : RJSConstructors(realm.get())) { + auto delegate = js::get_delegate(realm.get()); + for (auto pair : delegate->m_constructors) { if (pair.second == constructor) { return pair.first; } @@ -453,7 +368,7 @@ void RealmAddListener(ContextType ctx, ObjectType thisObject, size_t argumentCou auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->add_notification(callback); + static_cast *>(realm->m_binding_context.get())->add_notification(callback); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -467,7 +382,7 @@ void RealmRemoveListener(ContextType ctx, ObjectType thisObject, size_t argument auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_notification(callback); + static_cast *>(realm->m_binding_context.get())->remove_notification(callback); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -482,7 +397,7 @@ void RealmRemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argu } SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_all_notifications(); + static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); } catch (std::exception &exp) { RJSSetException(ctx, exceptionObject, exp); @@ -527,3 +442,10 @@ JSClassRef RJSRealmClass() { static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); return s_realmClass; } + + +namespace realm { +namespace js { +JSClassRef RealmClass() { return RJSRealmClass(); }; +} +} diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 167e64bd..54c1c443 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -19,11 +19,101 @@ #pragma once #include "js_util.hpp" +#include "shared_realm.hpp" +#include "binding_context.hpp" #include +#include namespace realm { - class Realm; - using ObjectDefaults = std::map; +namespace js { + +template +class RealmDelegate : public BindingContext { + public: + using GlobalContextType = typename T::GlobalContext; + using ObjectClassType = typename T::ObjectClass; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using ObjectDefaults = std::map; + + virtual void did_change(std::vector const& observers, std::vector const& invalidated) { + notify("change"); + } + virtual std::vector get_observed_rows() { + return std::vector(); + } + virtual void will_change(std::vector const& observers, std::vector const& invalidated) {} + + RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) { + GlobalContextProtect(m_context); + } + ~RealmDelegate() { + remove_all_notifications(); + + for (auto constructor : m_constructors) { + ValueUnprotect(m_context, constructor.second); + } + for (auto objectDefaults : m_defaults) { + for (auto value : objectDefaults.second) { + ValueUnprotect(m_context, value.second); + } + } + GlobalContextUnprotect(m_context); + } + + void add_notification(JSObjectRef notification) { + if (!m_notifications.count(notification)) { + ValueProtect(m_context, notification); + m_notifications.insert(notification); + } + } + void remove_notification(JSObjectRef notification) { + if (m_notifications.count(notification)) { + ValueUnprotect(m_context, notification); + m_notifications.erase(notification); + } + } + void remove_all_notifications() { + for (auto notification : m_notifications) { + ValueUnprotect(m_context, notification); + } + m_notifications.clear(); + } + + std::map m_defaults; + std::map m_constructors; + +private: + std::set m_notifications; + GlobalContextType m_context; + std::weak_ptr m_realm; + + void notify(const char *notification_name) { + JSValueRef arguments[2]; + SharedRealm realm = m_realm.lock(); + if (!realm) { + throw std::runtime_error("Realm no longer exists"); + } + ObjectType realm_object = WrapObject(m_context, realm::js::RealmClass(), new SharedRealm(realm)); + arguments[0] = realm_object; + arguments[1] = RJSValueForString(m_context, notification_name); + + for (auto callback : m_notifications) { + JSValueRef ex = NULL; + ObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, ex); + if (ex) { + throw RJSException(m_context, ex); + } + } + } +}; + +template +static RealmDelegate *get_delegate(Realm *realm) { + return dynamic_cast *>(realm->m_binding_context.get()); +} + +} } JSClassRef RJSRealmClass(); @@ -32,5 +122,3 @@ JSClassRef RJSRealmConstructorClass(); std::string RJSDefaultPath(); void RJSSetDefaultPath(std::string path); -std::map &RJSDefaults(realm::Realm *realm); -std::map &RJSConstructors(realm::Realm *realm); diff --git a/src/js_results.cpp b/src/js_results.cpp index a332f224..4ce6f159 100644 --- a/src/js_results.cpp +++ b/src/js_results.cpp @@ -96,7 +96,7 @@ JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef Results *copy = new Results(*results); copy->set_live(false); - return RJSWrapObject(ctx, RJSResultsClass(), copy); + return js::WrapObject(ctx, RJSResultsClass(), copy); } catch (std::exception &exp) { if (jsException) { @@ -144,7 +144,7 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl if (object_schema == realm->config().schema->end()) { throw std::runtime_error("Object type '" + className + "' not present in Realm."); } - return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); + return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); } JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className, std::string queryString, std::vector args) { @@ -159,14 +159,14 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string cl query_builder::ArgumentConverter arguments(ctx, args); query_builder::apply_predicate(query, predicate, arguments, schema, object_schema->name); - return RJSWrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query))); + return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query))); } JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, bool live) { Results *results = new Results(realm, objectSchema, std::move(query)); results->set_live(live); - return RJSWrapObject(ctx, RJSResultsClass(), results); + return js::WrapObject(ctx, js::ResultsClass(), results); } JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) { @@ -233,7 +233,7 @@ JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const Ob } Results *results = new Results(realm, objectSchema, std::move(query), {std::move(columns), std::move(ascending)}); - return RJSWrapObject(ctx, RJSResultsClass(), results); + return js::WrapObject(ctx, js::ResultsClass(), results); } static const JSStaticFunction RJSResultsFuncs[] = { @@ -247,3 +247,9 @@ JSClassRef RJSResultsClass() { static JSClassRef s_objectClass = RJSCreateWrapperClass("Results", ResultsGetProperty, ResultsSetProperty, RJSResultsFuncs, ResultsPropertyNames, RJSCollectionClass()); return s_objectClass; } + +namespace realm { +namespace js { +JSClassRef ResultsClass() { return RJSResultsClass(); }; +} +} diff --git a/src/js_schema.cpp b/src/js_schema.cpp index 1bbc83da..e803fcc9 100644 --- a/src/js_schema.cpp +++ b/src/js_schema.cpp @@ -42,7 +42,7 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { SchemaWrapper *wrapper = new SchemaWrapper(); wrapper->schema = &schema; wrapper->owned = false; - return RJSWrapObject(ctx, RJSSchemaClass(), wrapper); + return js::WrapObject(ctx, RJSSchemaClass(), wrapper); } static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) { diff --git a/src/js_util.hpp b/src/js_util.hpp index 72c891c6..83331eb1 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -53,15 +53,6 @@ inline void RJSFinalize(JSObjectRef object) { JSObjectSetPrivate(object, NULL); } -template -inline JSObjectRef RJSWrapObject(JSContextRef ctx, JSClassRef jsClass, T object, JSValueRef prototype = NULL) { - JSObjectRef ref = JSObjectMake(ctx, jsClass, (void *)object); - if (prototype) { - JSObjectSetPrototype(ctx, ref, prototype); - } - return ref; -} - template inline T RJSGetInternal(JSObjectRef jsObject) { return static_cast(JSObjectGetPrivate(jsObject)); diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index 8e36cd85..1065b23c 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -23,7 +23,7 @@ namespace realm { namespace js { - + static bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } static bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } static bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } @@ -31,4 +31,27 @@ static bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { re static bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } static bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } -}} \ No newline at end of file +static void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } +static void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } + +static void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { + JSObjectCallAsFunction(ctx, function, thisObject, argsCount, args, &exception); +} + +static void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGlobalContextRetain(ctx); } +static void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } + +template +static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { + JSObjectRef ref = JSObjectMake(ctx, objectClass, (void *)internal); + if (prototype) { + JSObjectSetPrototype(ctx, ref, prototype); + } + return ref; +} + +static jsc::Types::ObjectClass RealmClass(); +static jsc::Types::ObjectClass ListClass(); +static jsc::Types::ObjectClass ResultsClass(); + +}} diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index dc059cd6..33fe20c5 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -97,7 +97,7 @@ WRAP_CLASS_METHOD(RJSList, Filtered) WRAP_CLASS_METHOD(RJSList, Sorted) JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { - return RJSWrapObject(ctx, RJSListClass(), new List(list)); + return realm::js::WrapObject(ctx, realm::js::ListClass(), new List(list)); } static const JSStaticFunction RJSListFuncs[] = { @@ -116,3 +116,9 @@ JSClassRef RJSListClass() { static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass()); return s_listClass; } + +namespace realm { +namespace js { +JSClassRef ListClass() { return RJSListClass(); }; +} +} diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp index b5f8db3d..ffb8076a 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/types.hpp @@ -27,6 +27,8 @@ namespace jsc { struct Types { using Context = JSContextRef; + using GlobalContext = JSGlobalContextRef; + using ObjectClass = JSClassRef; using Value = JSValueRef; using Object = JSObjectRef; using String = JSStringRef; From 016d723731a5c771e57a3ce448ad2e46799b48d3 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 09:16:38 -0700 Subject: [PATCH 06/48] tmp --- src/ios/RealmJS.xcodeproj/project.pbxproj | 6 + src/js_init.cpp | 2 +- src/js_object.cpp | 6 + src/js_realm.cpp | 426 +--------------- src/js_realm.hpp | 245 ++++++++- src/js_results.cpp | 6 +- src/jsc/js_compat.hpp | 29 +- src/jsc/js_realm.cpp | 572 ---------------------- src/jsc/jsc_list.cpp | 4 +- src/jsc/jsc_realm.cpp | 235 +++++++++ src/jsc/{js_realm.hpp => jsc_realm.hpp} | 14 +- 11 files changed, 514 insertions(+), 1031 deletions(-) delete mode 100644 src/jsc/js_realm.cpp create mode 100644 src/jsc/jsc_realm.cpp rename src/jsc/{js_realm.hpp => jsc_realm.hpp} (69%) diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index b12a7d69..60941658 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; }; + 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 025678961CAB478800FB8501 /* jsc_realm.cpp */; }; 0270BC821B7D020100010E03 /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */; }; 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 029048121C0428DF00ABDED4 /* js_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* js_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -101,6 +102,8 @@ /* Begin PBXFileReference section */ 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = ""; }; 025678951CAB392000FB8501 /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = jsc/types.hpp; sourceTree = ""; }; + 025678961CAB478800FB8501 /* jsc_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_realm.cpp; path = jsc/jsc_realm.cpp; sourceTree = ""; }; + 025678971CAB478800FB8501 /* jsc_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_realm.hpp; path = jsc/jsc_realm.hpp; sourceTree = ""; }; 0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = ""; }; 0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = ""; }; @@ -265,6 +268,8 @@ 025678951CAB392000FB8501 /* types.hpp */, 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, + 025678961CAB478800FB8501 /* jsc_realm.cpp */, + 025678971CAB478800FB8501 /* jsc_realm.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, 0290480F1C0428DF00ABDED4 /* rpc.cpp */, 029048101C0428DF00ABDED4 /* rpc.hpp */, @@ -656,6 +661,7 @@ files = ( F6CB31001C8EDDAB0070EF3F /* js_collection.cpp in Sources */, 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */, + 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */, F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */, 02F59EC31C88F17D007F774C /* results.cpp in Sources */, 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */, diff --git a/src/js_init.cpp b/src/js_init.cpp index e000f17c..940ba9a6 100644 --- a/src/js_init.cpp +++ b/src/js_init.cpp @@ -17,7 +17,7 @@ //////////////////////////////////////////////////////////////////////////// #include "js_init.h" -#include "js_realm.hpp" +#include "jsc_realm.hpp" #include "js_object.hpp" #include "js_collection.hpp" #include "jsc_list.hpp" diff --git a/src/js_object.cpp b/src/js_object.cpp index 505a2505..c6c7b671 100644 --- a/src/js_object.cpp +++ b/src/js_object.cpp @@ -71,6 +71,12 @@ JSClassRef RJSObjectClass() { return s_objectClass; } +namespace realm { + namespace js { + JSClassRef object_class() { return RJSObjectClass(); }; + } +} + JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); diff --git a/src/js_realm.cpp b/src/js_realm.cpp index a9ce6f2f..5161da14 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -17,435 +17,21 @@ //////////////////////////////////////////////////////////////////////////// #include "js_realm.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "jsc_list.hpp" -#include "js_schema.hpp" #include "platform.hpp" -#include "shared_realm.hpp" -#include "impl/realm_coordinator.hpp" -#include "object_accessor.hpp" -#include "binding_context.hpp" -#include "results.hpp" +namespace realm { +namespace js { -#include - - -using namespace realm; -using RJSAccessor = realm::NativeAccessor; - -// static std::string s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; static std::string s_defaultPath = ""; -std::string RJSDefaultPath() { +std::string default_path() { if (s_defaultPath.size() == 0) { s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; } return s_defaultPath; } -void RJSSetDefaultPath(std::string path) { + +void set_default_path(std::string path) { s_defaultPath = path; } -static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - return RJSValueForString(ctx, s_defaultPath); -} - -static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { - try { - s_defaultPath = RJSValidatedStringForValue(ctx, value, "defaultPath"); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - } - return true; -} - -inline std::string RJSNormalizePath(std::string path) { - if (path.size() && path[0] != '/') { - return default_realm_file_directory() + "/" + path; - } - return path; -} - -JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Realm::Config config; - std::map defaults; - std::map constructors; - if (argumentCount == 0) { - config.path = RJSDefaultPath(); - } - else if (argumentCount == 1) { - JSValueRef value = arguments[0]; - if (JSValueIsString(ctx, value)) { - config.path = RJSValidatedStringForValue(ctx, value, "path"); - } - else if (JSValueIsObject(ctx, value)) { - JSObjectRef object = RJSValidatedValueToObject(ctx, value); - - static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); - JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); - if (!JSValueIsUndefined(ctx, pathValue)) { - config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); - } - else { - config.path = RJSDefaultPath(); - } - - static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); - JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); - if (!JSValueIsUndefined(ctx, schemaValue)) { - config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); - } - - static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); - JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); - if (JSValueIsNumber(ctx, versionValue)) { - config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); - } - else { - config.schema_version = 0; - } - - static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); - JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); - if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; - } - } - } - else { - *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); - return NULL; - } - - config.path = RJSNormalizePath(config.path); - - ensure_directory_exists_for_file(config.path); - SharedRealm realm = Realm::get_shared_realm(config); - auto delegate = new js::RealmDelegate(realm, JSContextGetGlobalContext(ctx)); - if (!realm->m_binding_context) { - realm->m_binding_context.reset(delegate); - } - delegate->m_defaults = defaults; - delegate->m_constructors = constructors; - return js::WrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - return NULL; - } -} - -bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { - return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); -} - -static const JSStaticValue RealmStaticProperties[] = { - {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} -}; - -template -void RealmSchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentRange(argumentCount, 1, 2); - - Realm::Config config; - config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); - if (argumentCount == 2) { - auto encryptionKeyValue = arguments[1]; - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); - } - - auto version = Realm::get_schema_version(config); - if (version == ObjectStore::NotVersioned) { - RJSSetReturnNumber(ctx, returnObject, -1); - } - else { - RJSSetReturnNumber(ctx, returnObject, version); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -WRAP_METHOD(RealmSchemaVersion) - -static const JSStaticFunction RealmConstructorFuncs[] = { - {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmConstructorClass() { - JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; - realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; - realmConstructorDefinition.className = "RealmConstructor"; - realmConstructorDefinition.callAsConstructor = RealmConstructor; - realmConstructorDefinition.hasInstance = RealmHasInstance; - realmConstructorDefinition.staticValues = RealmStaticProperties; - realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; - return JSClassCreate(&realmConstructorDefinition); -} - -JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - static JSStringRef s_pathString = JSStringCreateWithUTF8CString("path"); - if (JSStringIsEqual(propertyName, s_pathString)) { - return RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); - } - - static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion"); - if (JSStringIsEqual(propertyName, s_schemaVersion)) { - return JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); - } - return NULL; -} - -std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { - if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { - JSObjectRef constructor = (JSObjectRef)value; - - auto delegate = js::get_delegate(realm.get()); - for (auto pair : delegate->m_constructors) { - if (pair.second == constructor) { - return pair.first; - } - } - - throw std::runtime_error("Constructor was not registered in the schema for this Realm"); - } - - return RJSValidatedStringForValue(ctx, value, "objectType"); -} - -template -void RealmObjects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); - returnObject = RJSResultsCreate(ctx, sharedRealm, className); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmCreateObject(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentRange(argumentCount, 2, 3); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); - auto &schema = sharedRealm->config().schema; - auto object_schema = schema->find(className); - - if (object_schema == schema->end()) { - throw std::runtime_error("Object type '" + className + "' not found in schema."); - } - - auto object = RJSValidatedValueToObject(ctx, arguments[1]); - if (RJSIsValueArray(ctx, arguments[1])) { - object = RJSDictForPropertyArray(ctx, *object_schema, object); - } - - bool update = false; - if (argumentCount == 3) { - update = RJSValidatedValueToBoolean(ctx, arguments[2]); - } - - returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmDelete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 1); - - SharedRealm realm = *RJSGetInternal(thisObject); - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } - - auto arg = RJSValidatedValueToObject(ctx, arguments[0]); - if (RJSValueIsObjectOfClass(ctx, arg, RJSObjectClass())) { - Object *object = RJSGetInternal(arg); - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); - } - else if (RJSIsValueArray(ctx, arg)) { - size_t length = RJSValidatedListLength(ctx, arg); - for (long i = length-1; i >= 0; i--) { - JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); - if (!RJSValueIsObjectOfClass(ctx, jsObject, RJSObjectClass())) { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } - - Object *object = RJSGetInternal(jsObject); - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); - } - } - else if(RJSValueIsObjectOfClass(ctx, arg, RJSResultsClass())) { - Results *results = RJSGetInternal(arg); - results->clear(); - } - else if(RJSValueIsObjectOfClass(ctx, arg, RJSListClass())) { - List *list = RJSGetInternal(arg); - list->delete_all(); - } - else { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmDeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 0); - - SharedRealm realm = *RJSGetInternal(thisObject); - - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } - - for (auto objectSchema : *realm->config().schema) { - ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmWrite(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - SharedRealm realm = *RJSGetInternal(thisObject); - try { - RJSValidateArgumentCount(argumentCount, 1); - - auto object = RJSValidatedValueToFunction(ctx, arguments[0]); - realm->begin_transaction(); - RJSCallFunction(ctx, object, thisObject, 0, NULL); - realm->commit_transaction(); - } - catch (std::exception &exp) { - if (realm->is_in_transaction()) { - realm->cancel_transaction(); - } - RJSSetException(ctx, exceptionObject, exp); - } -} - -std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { - std::string name = RJSValidatedStringForValue(ctx, value); - if (name != "change") { - throw std::runtime_error("Only the 'change' notification name is supported."); - } - return name; -} - -template -void RealmAddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->add_notification(callback); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmRemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_notification(callback); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmRemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentRange(argumentCount, 0, 1); - if (argumentCount) { - RJSValidatedNotificationName(ctx, arguments[0]); - } - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void RealmClose(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try { - RJSValidateArgumentCount(argumentCount, 0); - SharedRealm realm = *RJSGetInternal(thisObject); - realm->close(); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -WRAP_METHOD(RealmObjects) -WRAP_METHOD(RealmCreateObject) -WRAP_METHOD(RealmDelete) -WRAP_METHOD(RealmDeleteAll) -WRAP_METHOD(RealmWrite) -WRAP_METHOD(RealmAddListener) -WRAP_METHOD(RealmRemoveListener) -WRAP_METHOD(RealmRemoveAllListeners) -WRAP_METHOD(RealmClose) - -static const JSStaticFunction RJSRealmFuncs[] = { - {"objects", RealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"create", RealmCreateObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"delete", RealmDelete, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"deleteAll", RealmDeleteAll, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"write", RealmWrite, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"addListener", RealmAddListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeListener", RealmRemoveListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeAllListeners", RealmRemoveAllListeners, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"close", RealmClose, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmClass() { - static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); - return s_realmClass; -} - - -namespace realm { -namespace js { -JSClassRef RealmClass() { return RJSRealmClass(); }; -} -} +}} diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 54c1c443..6d00359a 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -21,6 +21,8 @@ #include "js_util.hpp" #include "shared_realm.hpp" #include "binding_context.hpp" +#include "object_accessor.hpp" +#include "results.hpp" #include #include @@ -94,7 +96,7 @@ private: if (!realm) { throw std::runtime_error("Realm no longer exists"); } - ObjectType realm_object = WrapObject(m_context, realm::js::RealmClass(), new SharedRealm(realm)); + ObjectType realm_object = WrapObject(m_context, realm::js::realm_class(), new SharedRealm(realm)); arguments[0] = realm_object; arguments[1] = RJSValueForString(m_context, notification_name); @@ -113,12 +115,243 @@ static RealmDelegate *get_delegate(Realm *realm) { return dynamic_cast *>(realm->m_binding_context.get()); } -} +static inline std::string default_path(); +static inline void set_default_path(std::string path); + + +std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { + if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { + JSObjectRef constructor = (JSObjectRef)value; + + auto delegate = js::get_delegate(realm.get()); + for (auto pair : delegate->m_constructors) { + if (pair.second == constructor) { + return pair.first; + } + } + + throw std::runtime_error("Constructor was not registered in the schema for this Realm"); + } + + return RJSValidatedStringForValue(ctx, value, "objectType"); } -JSClassRef RJSRealmClass(); -JSClassRef RJSRealmConstructorClass(); -std::string RJSDefaultPath(); -void RJSSetDefaultPath(std::string path); +template +class Realm : public BindingContext { +public: + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using ReturnType = typename T::Return; + using ExceptionType = typename T::Exception; + static void Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + + static std::string validated_notification_name(JSContextRef ctx, JSValueRef value) { + std::string name = RJSValidatedStringForValue(ctx, value); + if (name != "change") { + throw std::runtime_error("Only the 'change' notification name is supported."); + } + return name; + } +}; + +template +void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 1); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + returnObject = RJSResultsCreate(ctx, sharedRealm, className); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentRange(argumentCount, 2, 3); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + auto &schema = sharedRealm->config().schema; + auto object_schema = schema->find(className); + + if (object_schema == schema->end()) { + throw std::runtime_error("Object type '" + className + "' not found in schema."); + } + + auto object = RJSValidatedValueToObject(ctx, arguments[1]); + if (RJSIsValueArray(ctx, arguments[1])) { + object = RJSDictForPropertyArray(ctx, *object_schema, object); + } + + bool update = false; + if (argumentCount == 3) { + update = RJSValidatedValueToBoolean(ctx, arguments[2]); + } + + returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 1); + + SharedRealm realm = *RJSGetInternal(thisObject); + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } + + auto arg = RJSValidatedValueToObject(ctx, arguments[0]); + if (RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { + Object *object = RJSGetInternal(arg); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + } + else if (RJSIsValueArray(ctx, arg)) { + size_t length = RJSValidatedListLength(ctx, arg); + for (long i = length-1; i >= 0; i--) { + JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); + if (!RJSValueIsObjectOfClass(ctx, jsObject, object_class())) { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } + + Object *object = RJSGetInternal(jsObject); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + } + } + else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { + Results *results = RJSGetInternal(arg); + results->clear(); + } + else if(RJSValueIsObjectOfClass(ctx, arg, list_class())) { + List *list = RJSGetInternal(arg); + list->delete_all(); + } + else { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 0); + + SharedRealm realm = *RJSGetInternal(thisObject); + + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } + + for (auto objectSchema : *realm->config().schema) { + ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); + } + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + SharedRealm realm = *RJSGetInternal(thisObject); + try { + RJSValidateArgumentCount(argumentCount, 1); + + auto object = RJSValidatedValueToFunction(ctx, arguments[0]); + realm->begin_transaction(); + RJSCallFunction(ctx, object, thisObject, 0, NULL); + realm->commit_transaction(); + } + catch (std::exception &exp) { + if (realm->is_in_transaction()) { + realm->cancel_transaction(); + } + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->add_notification(callback); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->remove_notification(callback); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentRange(argumentCount, 0, 1); + if (argumentCount) { + validated_notification_name(ctx, arguments[0]); + } + + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +template +void Realm::Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentCount(argumentCount, 0); + SharedRealm realm = *RJSGetInternal(thisObject); + realm->close(); + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +} +} \ No newline at end of file diff --git a/src/js_results.cpp b/src/js_results.cpp index 4ce6f159..b9ac58af 100644 --- a/src/js_results.cpp +++ b/src/js_results.cpp @@ -166,7 +166,7 @@ JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSc Results *results = new Results(realm, objectSchema, std::move(query)); results->set_live(live); - return js::WrapObject(ctx, js::ResultsClass(), results); + return js::WrapObject(ctx, js::results_class(), results); } JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) { @@ -233,7 +233,7 @@ JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const Ob } Results *results = new Results(realm, objectSchema, std::move(query), {std::move(columns), std::move(ascending)}); - return js::WrapObject(ctx, js::ResultsClass(), results); + return js::WrapObject(ctx, js::results_class(), results); } static const JSStaticFunction RJSResultsFuncs[] = { @@ -250,6 +250,6 @@ JSClassRef RJSResultsClass() { namespace realm { namespace js { -JSClassRef ResultsClass() { return RJSResultsClass(); }; +JSClassRef results_class() { return RJSResultsClass(); }; } } diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index 1065b23c..c7453f19 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -24,22 +24,22 @@ namespace realm { namespace js { -static bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } -static bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } -static bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } -static bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNumber(ctx, value); } -static bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } -static bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } +static inline bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } +static inline bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } +static inline bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } +static inline bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNumber(ctx, value); } +static inline bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } +static inline bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } -static void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } -static void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } +static inline void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } +static inline void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } -static void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { +static inline void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { JSObjectCallAsFunction(ctx, function, thisObject, argsCount, args, &exception); } -static void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGlobalContextRetain(ctx); } -static void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } +static inline void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGlobalContextRetain(ctx); } +static inline void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } template static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { @@ -50,8 +50,9 @@ static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::Object return ref; } -static jsc::Types::ObjectClass RealmClass(); -static jsc::Types::ObjectClass ListClass(); -static jsc::Types::ObjectClass ResultsClass(); +static inline jsc::Types::ObjectClass realm_class(); +static inline jsc::Types::ObjectClass list_class(); +static inline jsc::Types::ObjectClass object_class(); +static inline jsc::Types::ObjectClass results_class(); }} diff --git a/src/jsc/js_realm.cpp b/src/jsc/js_realm.cpp deleted file mode 100644 index 5929c435..00000000 --- a/src/jsc/js_realm.cpp +++ /dev/null @@ -1,572 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_realm.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "jsc_list.hpp" -#include "js_schema.hpp" -#include "platform.hpp" - -#include "shared_realm.hpp" -#include "impl/realm_coordinator.hpp" -#include "object_accessor.hpp" -#include "binding_context.hpp" - -#include -#include - -using namespace realm; -using RJSAccessor = realm::NativeAccessor; - -class RJSRealmDelegate : public BindingContext { -public: - virtual void changes_available() { - assert(0); - } - virtual void did_change(std::vector const& observers, std::vector const& invalidated) { - notify("change"); - } - virtual std::vector get_observed_rows() { - return std::vector(); - } - virtual void will_change(std::vector const& observers, - std::vector const& invalidated) {} - - RJSRealmDelegate(WeakRealm realm, JSGlobalContextRef ctx) : m_context(ctx), m_realm(realm) { - JSGlobalContextRetain(m_context); - } - - ~RJSRealmDelegate() { - remove_all_notifications(); - - for (auto constructor : m_constructors) { - JSValueUnprotect(m_context, constructor.second); - } - for (auto objectDefaults : m_defaults) { - for (auto value : objectDefaults.second) { - JSValueUnprotect(m_context, value.second); - } - } - JSGlobalContextRelease(m_context); - } - - void add_notification(JSObjectRef notification) { - if (!m_notifications.count(notification)) { - JSValueProtect(m_context, notification); - m_notifications.insert(notification); - } - } - void remove_notification(JSObjectRef notification) { - if (m_notifications.count(notification)) { - JSValueUnprotect(m_context, notification); - m_notifications.erase(notification); - } - } - void remove_all_notifications() { - for (auto notification : m_notifications) { - JSValueUnprotect(m_context, notification); - } - m_notifications.clear(); - } - - std::map m_defaults; - std::map m_constructors; - - private: - std::set m_notifications; - JSGlobalContextRef m_context; - WeakRealm m_realm; - - void notify(const char *notification_name) { - JSValueRef arguments[2]; - SharedRealm realm = m_realm.lock(); - if (!realm) { - throw std::runtime_error("Realm no longer exists"); - } - JSObjectRef realm_object = RJSWrapObject(m_context, RJSRealmClass(), new SharedRealm(realm)); - arguments[0] = realm_object; - arguments[1] = RJSValueForString(m_context, notification_name); - - for (auto callback : m_notifications) { - JSValueRef ex = NULL; - JSObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, &ex); - if (ex) { - throw RJSException(m_context, ex); - } - } - } -}; - -std::map &RJSDefaults(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_defaults; -} - -std::map &RJSConstructors(Realm *realm) { - return static_cast(realm->m_binding_context.get())->m_constructors; -} - -// static std::string s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; -static std::string s_defaultPath = ""; -std::string RJSDefaultPath() { - if (s_defaultPath.size() == 0) { - s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; - } - return s_defaultPath; -} -void RJSSetDefaultPath(std::string path) { - s_defaultPath = path; -} - -static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - return RJSValueForString(ctx, s_defaultPath); -} - -static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { - try { - s_defaultPath = RJSValidatedStringForValue(ctx, value, "defaultPath"); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - } - return true; -} - -inline std::string RJSNormalizePath(std::string path) { - if (path.size() && path[0] != '/') { - return default_realm_file_directory() + "/" + path; - } - return path; -} - -JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Realm::Config config; - std::map defaults; - std::map constructors; - if (argumentCount == 0) { - config.path = RJSDefaultPath(); - } - else if (argumentCount == 1) { - JSValueRef value = arguments[0]; - if (JSValueIsString(ctx, value)) { - config.path = RJSValidatedStringForValue(ctx, value, "path"); - } - else if (JSValueIsObject(ctx, value)) { - JSObjectRef object = RJSValidatedValueToObject(ctx, value); - - static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); - JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); - if (!JSValueIsUndefined(ctx, pathValue)) { - config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); - } - else { - config.path = RJSDefaultPath(); - } - - static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); - JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); - if (!JSValueIsUndefined(ctx, schemaValue)) { - config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); - } - - static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); - JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); - if (JSValueIsNumber(ctx, versionValue)) { - config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); - } - else { - config.schema_version = 0; - } - - static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); - JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); - if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; - } - } - } - else { - *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); - return NULL; - } - - config.path = RJSNormalizePath(config.path); - - ensure_directory_exists_for_file(config.path); - SharedRealm realm = Realm::get_shared_realm(config); - if (!realm->m_binding_context) { - realm->m_binding_context.reset(new RJSRealmDelegate(realm, JSContextGetGlobalContext(ctx))); - } - RJSDefaults(realm.get()) = defaults; - RJSConstructors(realm.get()) = constructors; - return RJSWrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - return NULL; - } -} - -bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { - return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); -} - -static const JSStaticValue RealmStaticProperties[] = { - {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} -}; - -JSValueRef RealmSchemaVersion(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentRange(argumentCount, 1, 2); - - Realm::Config config; - config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); - if (argumentCount == 2) { - JSValueRef encryptionKeyValue = arguments[1]; - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); - } - - auto version = Realm::get_schema_version(config); - if (version == ObjectStore::NotVersioned) { - return JSValueMakeNumber(ctx, -1); - } - else { - return JSValueMakeNumber(ctx, version); - } - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - } - return NULL; -} - -static const JSStaticFunction RealmConstructorFuncs[] = { - {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmConstructorClass() { - JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; - realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; - realmConstructorDefinition.className = "RealmConstructor"; - realmConstructorDefinition.callAsConstructor = RealmConstructor; - realmConstructorDefinition.hasInstance = RealmHasInstance; - realmConstructorDefinition.staticValues = RealmStaticProperties; - realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; - return JSClassCreate(&realmConstructorDefinition); -} - -JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - static JSStringRef s_pathString = JSStringCreateWithUTF8CString("path"); - if (JSStringIsEqual(propertyName, s_pathString)) { - return RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); - } - - static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion"); - if (JSStringIsEqual(propertyName, s_schemaVersion)) { - return JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); - } - return NULL; -} - -std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { - if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { - JSObjectRef constructor = (JSObjectRef)value; - - for (auto pair : RJSConstructors(realm.get())) { - if (pair.second == constructor) { - return pair.first; - } - } - - throw std::runtime_error("Constructor was not registered in the schema for this Realm"); - } - - return RJSValidatedStringForValue(ctx, value, "objectType"); -} - -JSValueRef RealmObjects(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); - return RJSResultsCreate(ctx, sharedRealm, className); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const ObjectSchema &object_schema, JSObjectRef array) { - // copy to dictionary - if (object_schema.properties.size() != RJSValidatedListLength(ctx, array)) { - throw std::runtime_error("Array must contain values for all object properties"); - } - - JSValueRef exception = NULL; - JSObjectRef dict = JSObjectMake(ctx, NULL, NULL); - for (unsigned int i = 0; i < object_schema.properties.size(); i++) { - JSStringRef nameStr = JSStringCreateWithUTF8CString(object_schema.properties[i].name.c_str()); - JSValueRef value = JSObjectGetPropertyAtIndex(ctx, array, i, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSObjectSetProperty(ctx, dict, nameStr, value, kJSPropertyAttributeNone, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSStringRelease(nameStr); - } - return dict; -} - -JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentRange(argumentCount, 2, 3); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); - auto &schema = sharedRealm->config().schema; - auto object_schema = schema->find(className); - - if (object_schema == schema->end()) { - *jsException = RJSMakeError(ctx, "Object type '" + className + "' not found in schema."); - return NULL; - } - - JSObjectRef object = RJSValidatedValueToObject(ctx, arguments[1]); - if (RJSIsValueArray(ctx, arguments[1])) { - object = RJSDictForPropertyArray(ctx, *object_schema, object); - } - - bool update = false; - if (argumentCount == 3) { - update = JSValueToBoolean(ctx, arguments[2]); - } - - return RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmDelete(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 1); - - if (RJSIsValueArray(ctx, arguments[0]) || - JSValueIsObjectOfClass(ctx, arguments[0], RJSResultsClass()) || - JSValueIsObjectOfClass(ctx, arguments[0], RJSListClass())) - { - JSObjectRef array = RJSValidatedValueToObject(ctx, arguments[0]); - size_t length = RJSValidatedListLength(ctx, array); - for (long i = length-1; i >= 0; i--) { - JSValueRef object = RJSValidatedObjectAtIndex(ctx, array, (unsigned int)i); - RealmDelete(ctx, function, thisObject, 1, &object, jsException); - if (*jsException) { - return NULL; - } - } - return NULL; - } - - if (!JSValueIsObjectOfClass(ctx, arguments[0], RJSObjectClass())) { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } - - Object *object = RJSGetInternal(RJSValidatedValueToObject(ctx, arguments[0])); - - SharedRealm realm = *RJSGetInternal(thisObject); - - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } - - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); - - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmDeleteAll(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 0); - - SharedRealm realm = *RJSGetInternal(thisObject); - - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } - - for (auto objectSchema : *realm->config().schema) { - ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); - } - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmWrite(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 1); - - JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]); - SharedRealm realm = *RJSGetInternal(thisObject); - realm->begin_transaction(); - JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException); - if (*jsException) { - realm->cancel_transaction(); - } - else { - realm->commit_transaction(); - } - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } - - return NULL; -} - -std::string RJSValidatedNotificationName(JSContextRef ctx, JSValueRef value) { - std::string name = RJSValidatedStringForValue(ctx, value); - if (name != "change") { - throw std::runtime_error("Only the 'change' notification name is supported."); - } - return name; -} - -JSValueRef RealmAddListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->add_notification(callback); - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmRemoveListener(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = RJSValidatedNotificationName(ctx, arguments[0]); - JSObjectRef callback = RJSValidatedValueToFunction(ctx, arguments[1]); - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_notification(callback); - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmRemoveAllListeners(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentRange(argumentCount, 0, 1); - if (argumentCount) { - RJSValidatedNotificationName(ctx, arguments[0]); - } - - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast(realm->m_binding_context.get())->remove_all_notifications(); - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - RJSValidateArgumentCount(argumentCount, 0); - SharedRealm realm = *RJSGetInternal(thisObject); - realm->close(); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - } - return NULL; -} - -static const JSStaticFunction RJSRealmFuncs[] = { - {"objects", RealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"create", RealmCreateObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"delete", RealmDelete, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"deleteAll", RealmDeleteAll, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"write", RealmWrite, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"addListener", RealmAddListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeListener", RealmRemoveListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeAllListeners", RealmRemoveAllListeners, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"close", RealmClose, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmClass() { - static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); - return s_realmClass; -} diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index 33fe20c5..1a7781f3 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -97,7 +97,7 @@ WRAP_CLASS_METHOD(RJSList, Filtered) WRAP_CLASS_METHOD(RJSList, Sorted) JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { - return realm::js::WrapObject(ctx, realm::js::ListClass(), new List(list)); + return realm::js::WrapObject(ctx, realm::js::list_class(), new List(list)); } static const JSStaticFunction RJSListFuncs[] = { @@ -119,6 +119,6 @@ JSClassRef RJSListClass() { namespace realm { namespace js { -JSClassRef ListClass() { return RJSListClass(); }; +JSClassRef list_class() { return RJSListClass(); }; } } diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp new file mode 100644 index 00000000..6e939381 --- /dev/null +++ b/src/jsc/jsc_realm.cpp @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "jsc_realm.hpp" +#include "js_realm.hpp" +#include "js_object.hpp" +#include "js_results.hpp" +#include "jsc_list.hpp" +#include "js_schema.hpp" +#include "platform.hpp" + +#include "shared_realm.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_accessor.hpp" +#include "binding_context.hpp" +#include "results.hpp" + +#include + +using namespace realm; +using RJSAccessor = realm::NativeAccessor; + + +static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { + return RJSValueForString(ctx, realm::js::default_path()); +} + +static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { + try { + realm::js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); + } + catch (std::exception &ex) { + if (jsException) { + *jsException = RJSMakeError(ctx, ex); + } + } + return true; +} + +inline std::string RJSNormalizePath(std::string path) { + if (path.size() && path[0] != '/') { + return default_realm_file_directory() + "/" + path; + } + return path; +} + +JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { + try { + Realm::Config config; + std::map defaults; + std::map constructors; + if (argumentCount == 0) { + config.path = js::default_path(); + } + else if (argumentCount == 1) { + JSValueRef value = arguments[0]; + if (JSValueIsString(ctx, value)) { + config.path = RJSValidatedStringForValue(ctx, value, "path"); + } + else if (JSValueIsObject(ctx, value)) { + JSObjectRef object = RJSValidatedValueToObject(ctx, value); + + static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); + JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); + if (!JSValueIsUndefined(ctx, pathValue)) { + config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); + } + else { + config.path = js::default_path(); + } + + static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); + JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); + if (!JSValueIsUndefined(ctx, schemaValue)) { + config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); + } + + static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); + JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); + if (JSValueIsNumber(ctx, versionValue)) { + config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); + } + else { + config.schema_version = 0; + } + + static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); + JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); + if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; + } + } + } + else { + *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); + return NULL; + } + + config.path = RJSNormalizePath(config.path); + + ensure_directory_exists_for_file(config.path); + SharedRealm realm = Realm::get_shared_realm(config); + auto delegate = new js::RealmDelegate(realm, JSContextGetGlobalContext(ctx)); + if (!realm->m_binding_context) { + realm->m_binding_context.reset(delegate); + } + delegate->m_defaults = defaults; + delegate->m_constructors = constructors; + return js::WrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); + } + catch (std::exception &ex) { + if (jsException) { + *jsException = RJSMakeError(ctx, ex); + } + return NULL; + } +} + +bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { + return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); +} + +static const JSStaticValue RealmStaticProperties[] = { + {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL} +}; + +template +void RealmSchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { + try { + RJSValidateArgumentRange(argumentCount, 1, 2); + + Realm::Config config; + config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); + if (argumentCount == 2) { + auto encryptionKeyValue = arguments[1]; + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); + } + + auto version = Realm::get_schema_version(config); + if (version == ObjectStore::NotVersioned) { + RJSSetReturnNumber(ctx, returnObject, -1); + } + else { + RJSSetReturnNumber(ctx, returnObject, version); + } + } + catch (std::exception &exp) { + RJSSetException(ctx, exceptionObject, exp); + } +} + +WRAP_METHOD(RealmSchemaVersion) + +static const JSStaticFunction RealmConstructorFuncs[] = { + {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + +JSClassRef RJSRealmConstructorClass() { + JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; + realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; + realmConstructorDefinition.className = "RealmConstructor"; + realmConstructorDefinition.callAsConstructor = RealmConstructor; + realmConstructorDefinition.hasInstance = RealmHasInstance; + realmConstructorDefinition.staticValues = RealmStaticProperties; + realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; + return JSClassCreate(&realmConstructorDefinition); +} + +JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { + static JSStringRef s_pathString = JSStringCreateWithUTF8CString("path"); + if (JSStringIsEqual(propertyName, s_pathString)) { + return RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); + } + + static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion"); + if (JSStringIsEqual(propertyName, s_schemaVersion)) { + return JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); + } + return NULL; +} + +using RJSRealm = realm::js::Realm; +WRAP_CLASS_METHOD(RJSRealm, Objects) +WRAP_CLASS_METHOD(RJSRealm, Create) +WRAP_CLASS_METHOD(RJSRealm, Delete) +WRAP_CLASS_METHOD(RJSRealm, DeleteAll) +WRAP_CLASS_METHOD(RJSRealm, Write) +WRAP_CLASS_METHOD(RJSRealm, AddListener) +WRAP_CLASS_METHOD(RJSRealm, RemoveListener) +WRAP_CLASS_METHOD(RJSRealm, RemoveAllListeners) +WRAP_CLASS_METHOD(RJSRealm, Close) + +static const JSStaticFunction RJSRealmFuncs[] = { + {"objects", RJSRealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"create", RJSRealmCreate, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"delete", RJSRealmDelete, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"deleteAll", RJSRealmDeleteAll, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"write", RJSRealmWrite, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"addListener", RJSRealmAddListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"removeListener", RJSRealmRemoveListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"removeAllListeners", RJSRealmRemoveAllListeners, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"close", RJSRealmClose, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + +JSClassRef RJSRealmClass() { + static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); + return s_realmClass; +} + + +namespace realm { +namespace js { +JSClassRef realm_class() { return RJSRealmClass(); }; +} +} diff --git a/src/jsc/js_realm.hpp b/src/jsc/jsc_realm.hpp similarity index 69% rename from src/jsc/js_realm.hpp rename to src/jsc/jsc_realm.hpp index 167e64bd..ea5dbabc 100644 --- a/src/jsc/js_realm.hpp +++ b/src/jsc/jsc_realm.hpp @@ -18,19 +18,7 @@ #pragma once -#include "js_util.hpp" -#include - -namespace realm { - class Realm; - using ObjectDefaults = std::map; -} +#include "types.hpp" JSClassRef RJSRealmClass(); JSClassRef RJSRealmConstructorClass(); - -std::string RJSDefaultPath(); -void RJSSetDefaultPath(std::string path); - -std::map &RJSDefaults(realm::Realm *realm); -std::map &RJSConstructors(realm::Realm *realm); From 58d50bb3c58db19c941d5a4e1edff50f9b030fba Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 10:23:15 -0700 Subject: [PATCH 07/48] refactor realm --- src/js_realm.hpp | 51 +++++++++++++++++++++---------------------- src/jsc/js_compat.hpp | 15 ++++++++----- src/jsc/jsc_list.cpp | 6 +---- src/jsc/jsc_realm.cpp | 2 +- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 6d00359a..29d6b04d 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -111,31 +111,12 @@ private: }; template -static RealmDelegate *get_delegate(Realm *realm) { +RealmDelegate *get_delegate(Realm *realm) { return dynamic_cast *>(realm->m_binding_context.get()); } -static inline std::string default_path(); -static inline void set_default_path(std::string path); - - -std::string RJSValidatedObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { - if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) { - JSObjectRef constructor = (JSObjectRef)value; - - auto delegate = js::get_delegate(realm.get()); - for (auto pair : delegate->m_constructors) { - if (pair.second == constructor) { - return pair.first; - } - } - - throw std::runtime_error("Constructor was not registered in the schema for this Realm"); - } - - return RJSValidatedStringForValue(ctx, value, "objectType"); -} - +std::string default_path(); +void set_default_path(std::string path); template class Realm : public BindingContext { @@ -163,6 +144,24 @@ public: } return name; } + + // converts constructor object or type name to type name + static std::string validated_object_type_for_value(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { + if (ValueIsObject(ctx, value) && ValueIsConstructor(ctx, value)) { + ObjectType constructor = ValueToObject(ctx, value); + + auto delegate = get_delegate(realm.get()); + for (auto pair : delegate->m_constructors) { + if (pair.second == constructor) { + return pair.first; + } + } + throw std::runtime_error("Constructor was not registered in the schema for this Realm"); + } + return RJSValidatedStringForValue(ctx, value, "objectType"); + } + + }; template @@ -171,7 +170,7 @@ void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCo RJSValidateArgumentCount(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); returnObject = RJSResultsCreate(ctx, sharedRealm, className); } catch (std::exception &exp) { @@ -185,7 +184,7 @@ void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCou RJSValidateArgumentRange(argumentCount, 2, 3); SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]); + std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); auto &schema = sharedRealm->config().schema; auto object_schema = schema->find(className); @@ -221,7 +220,7 @@ void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCou } auto arg = RJSValidatedValueToObject(ctx, arguments[0]); - if (RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { + if (RJSValueIsObjectOfClass(ctx, arg, realm::js::object_class())) { Object *object = RJSGetInternal(arg); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); table->move_last_over(object->row().get_index()); @@ -243,7 +242,7 @@ void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCou Results *results = RJSGetInternal(arg); results->clear(); } - else if(RJSValueIsObjectOfClass(ctx, arg, list_class())) { + else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::list_class())) { List *list = RJSGetInternal(arg); list->delete_all(); } diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index c7453f19..0cd54352 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -30,7 +30,10 @@ static inline bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value val static inline bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNumber(ctx, value); } static inline bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } static inline bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } - +static inline bool ValueIsConstructor(jsc::Types::Context ctx, jsc::Types::Value value) { return ValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); } + +static inline jsc::Types::Object ValueToObject(jsc::Types::Context ctx, jsc::Types::Value value) { return (JSObjectRef)value; } + static inline void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } static inline void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } @@ -42,7 +45,7 @@ static inline void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGloba static inline void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } template -static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { +jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { JSObjectRef ref = JSObjectMake(ctx, objectClass, (void *)internal); if (prototype) { JSObjectSetPrototype(ctx, ref, prototype); @@ -50,9 +53,9 @@ static jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::Object return ref; } -static inline jsc::Types::ObjectClass realm_class(); -static inline jsc::Types::ObjectClass list_class(); -static inline jsc::Types::ObjectClass object_class(); -static inline jsc::Types::ObjectClass results_class(); +jsc::Types::ObjectClass realm_class(); +jsc::Types::ObjectClass list_class(); +jsc::Types::ObjectClass object_class(); +jsc::Types::ObjectClass results_class(); }} diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index 1a7781f3..5f4f2802 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -117,8 +117,4 @@ JSClassRef RJSListClass() { return s_listClass; } -namespace realm { -namespace js { -JSClassRef list_class() { return RJSListClass(); }; -} -} +JSClassRef realm::js::list_class() { return RJSListClass(); }; diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp index 6e939381..258ffe51 100644 --- a/src/jsc/jsc_realm.cpp +++ b/src/jsc/jsc_realm.cpp @@ -42,7 +42,7 @@ static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringR static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { try { - realm::js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); + js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); } catch (std::exception &ex) { if (jsException) { From 0282e9823256d16e1dc86d71b56c15bbb975e802 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 11:55:13 -0700 Subject: [PATCH 08/48] move exception handling to method wrapper --- src/js_list.hpp | 211 ++++++++++++++++----------------------- src/js_realm.hpp | 252 ++++++++++++++++++++--------------------------- src/js_util.hpp | 3 +- 3 files changed, 193 insertions(+), 273 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index 76a53dd7..dbfdaf83 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -41,165 +41,124 @@ struct List { using ObjectType = typename T::Object; using ValueType = typename T::Value; using ReturnType = typename T::Return; - using ExceptionType = typename T::Exception; - static void Push(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Pop(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Unshift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Shift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Splice(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void StaticResults(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Filtered(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); - static void Sorted(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret, ExceptionType &exception); + static void Push(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Pop(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Unshift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Shift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Splice(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void StaticResults(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Filtered(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static void Sorted(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); }; template -void List::Push(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { - list->add(ctx, arguments[i]); - } - RJSSetReturnNumber(ctx, returnObject, list->size()); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); +void List::Push(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + list->add(ctx, arguments[i]); } + RJSSetReturnNumber(ctx, returnObject, list->size()); } template -void List::Pop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - - size_t size = list->size(); - if (size == 0) { - list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); - } - else { - size_t index = size - 1; - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); - list->remove(index); - } +void List::Pop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + + size_t size = list->size(); + if (size == 0) { + list->verify_in_transaction(); + RJSSetReturnUndefined(ctx, returnObject); } - catch (std::exception &exception) { - RJSSetException(ctx, exceptionObject, exception); + else { + size_t index = size - 1; + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + list->remove(index); } } template -void List::Unshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { - list->insert(ctx, arguments[i], i); - } - RJSSetReturnNumber(ctx, returnObject, list->size()); +void List::Unshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + for (size_t i = 0; i < argumentCount; i++) { + list->insert(ctx, arguments[i], i); } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); + RJSSetReturnNumber(ctx, returnObject, list->size()); +} + +template +void List::Shift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + if (list->size() == 0) { + list->verify_in_transaction(); + RJSSetReturnUndefined(ctx, returnObject); + } + else { + returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); + list->remove(0); } } template -void List::Shift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - if (list->size() == 0) { - list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); - } - else { - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); - list->remove(0); - } +void List::Splice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + size_t size = list->size(); + + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + long index = std::min(RJSValidatedValueToNumber(ctx, arguments[0]), size); + if (index < 0) { + index = std::max(size + index, 0); } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); + + long remove; + if (argumentCount < 2) { + remove = size - index; } -} - -template -void List::Splice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - size_t size = list->size(); - - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - long index = std::min(RJSValidatedValueToNumber(ctx, arguments[0]), size); - if (index < 0) { - index = std::max(size + index, 0); - } - - long remove; - if (argumentCount < 2) { - remove = size - index; - } - else { - remove = std::max(RJSValidatedValueToNumber(ctx, arguments[1]), 0); - remove = std::min(remove, size - index); - } - - std::vector removedObjects(remove); - for (size_t i = 0; i < remove; i++) { - removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); - list->remove(index); - } - for (size_t i = 2; i < argumentCount; i++) { - list->insert(ctx, arguments[i], index + i - 2); - } - RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); + else { + remove = std::max(RJSValidatedValueToNumber(ctx, arguments[1]), 0); + remove = std::min(remove, size - index); } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); + + std::vector removedObjects(remove); + for (size_t i = 0; i < remove; i++) { + removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + list->remove(index); } + for (size_t i = 2; i < argumentCount; i++) { + list->insert(ctx, arguments[i], index + i - 2); + } + RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); } template -void List::StaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } +void List::StaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCount(argumentCount, 0); + returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); } template -void List::Filtered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } +void List::Filtered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); } template -void List::Sorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentRange(argumentCount, 1, 2); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } +void List::Sorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + realm::List *list = RJSGetInternal(thisObject); + RJSValidateArgumentRange(argumentCount, 1, 2); + + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); } } diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 29d6b04d..0702391b 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -127,15 +127,15 @@ public: using ReturnType = typename T::Return; using ExceptionType = typename T::Exception; - static void Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); - static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject); + static void Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static std::string validated_notification_name(JSContextRef ctx, JSValueRef value) { std::string name = RJSValidatedStringForValue(ctx, value); @@ -165,123 +165,103 @@ public: }; template -void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 1); +void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 1); - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); - returnObject = RJSResultsCreate(ctx, sharedRealm, className); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); + returnObject = RJSResultsCreate(ctx, sharedRealm, className); } template -void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentRange(argumentCount, 2, 3); +void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentRange(argumentCount, 2, 3); - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); - auto &schema = sharedRealm->config().schema; - auto object_schema = schema->find(className); + SharedRealm sharedRealm = *RJSGetInternal(thisObject); + std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); + auto &schema = sharedRealm->config().schema; + auto object_schema = schema->find(className); - if (object_schema == schema->end()) { - throw std::runtime_error("Object type '" + className + "' not found in schema."); - } - - auto object = RJSValidatedValueToObject(ctx, arguments[1]); - if (RJSIsValueArray(ctx, arguments[1])) { - object = RJSDictForPropertyArray(ctx, *object_schema, object); - } - - bool update = false; - if (argumentCount == 3) { - update = RJSValidatedValueToBoolean(ctx, arguments[2]); - } - - returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + if (object_schema == schema->end()) { + throw std::runtime_error("Object type '" + className + "' not found in schema."); } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); + + auto object = RJSValidatedValueToObject(ctx, arguments[1]); + if (RJSIsValueArray(ctx, arguments[1])) { + object = RJSDictForPropertyArray(ctx, *object_schema, object); } + + bool update = false; + if (argumentCount == 3) { + update = RJSValidatedValueToBoolean(ctx, arguments[2]); + } + + returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); } template -void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 1); +void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 1); - SharedRealm realm = *RJSGetInternal(thisObject); - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } + SharedRealm realm = *RJSGetInternal(thisObject); + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } - auto arg = RJSValidatedValueToObject(ctx, arguments[0]); - if (RJSValueIsObjectOfClass(ctx, arg, realm::js::object_class())) { - Object *object = RJSGetInternal(arg); + auto arg = RJSValidatedValueToObject(ctx, arguments[0]); + if (RJSValueIsObjectOfClass(ctx, arg, realm::js::object_class())) { + Object *object = RJSGetInternal(arg); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); + table->move_last_over(object->row().get_index()); + } + else if (RJSIsValueArray(ctx, arg)) { + size_t length = RJSValidatedListLength(ctx, arg); + for (long i = length-1; i >= 0; i--) { + JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); + if (!RJSValueIsObjectOfClass(ctx, jsObject, object_class())) { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); + } + + Object *object = RJSGetInternal(jsObject); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); table->move_last_over(object->row().get_index()); } - else if (RJSIsValueArray(ctx, arg)) { - size_t length = RJSValidatedListLength(ctx, arg); - for (long i = length-1; i >= 0; i--) { - JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); - if (!RJSValueIsObjectOfClass(ctx, jsObject, object_class())) { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } - - Object *object = RJSGetInternal(jsObject); - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); - } - } - else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { - Results *results = RJSGetInternal(arg); - results->clear(); - } - else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::list_class())) { - List *list = RJSGetInternal(arg); - list->delete_all(); - } - else { - throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); - } } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); + else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { + Results *results = RJSGetInternal(arg); + results->clear(); + } + else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::list_class())) { + List *list = RJSGetInternal(arg); + list->delete_all(); + } + else { + throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); } } template -void Realm::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 0); +void Realm::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 0); - SharedRealm realm = *RJSGetInternal(thisObject); - - if (!realm->is_in_transaction()) { - throw std::runtime_error("Can only delete objects within a transaction."); - } - - for (auto objectSchema : *realm->config().schema) { - ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -template -void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { SharedRealm realm = *RJSGetInternal(thisObject); - try { - RJSValidateArgumentCount(argumentCount, 1); - auto object = RJSValidatedValueToFunction(ctx, arguments[0]); + if (!realm->is_in_transaction()) { + throw std::runtime_error("Can only delete objects within a transaction."); + } + + for (auto objectSchema : *realm->config().schema) { + ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear(); + } +} + +template +void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 1); + + SharedRealm realm = *RJSGetInternal(thisObject); + auto object = RJSValidatedValueToFunction(ctx, arguments[0]); + try { realm->begin_transaction(); RJSCallFunction(ctx, object, thisObject, 0, NULL); realm->commit_transaction(); @@ -290,66 +270,46 @@ void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCoun if (realm->is_in_transaction()) { realm->cancel_transaction(); } - RJSSetException(ctx, exceptionObject, exp); + throw; } } template -void Realm::AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = validated_notification_name(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); +void Realm::AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->add_notification(callback); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->add_notification(callback); } template -void Realm::RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = validated_notification_name(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); +void Realm::RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 2); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_notification(callback); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->remove_notification(callback); } template -void Realm::RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentRange(argumentCount, 0, 1); - if (argumentCount) { - validated_notification_name(ctx, arguments[0]); - } +void Realm::RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentRange(argumentCount, 0, 1); + if (argumentCount) { + validated_notification_name(ctx, arguments[0]); + } - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } + SharedRealm realm = *RJSGetInternal(thisObject); + static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); } template -void Realm::Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentCount(argumentCount, 0); - SharedRealm realm = *RJSGetInternal(thisObject); - realm->close(); - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } +void Realm::Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + RJSValidateArgumentCount(argumentCount, 0); + SharedRealm realm = *RJSGetInternal(thisObject); + realm->close(); } } diff --git a/src/js_util.hpp b/src/js_util.hpp index 83331eb1..8c8789ce 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -36,7 +36,8 @@ #define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ JSValueRef returnObject = NULL; \ - CLASS_NAME::METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, *jsException); \ + try { CLASS_NAME::METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject); } \ + catch(std::exception &ex) { RJSSetException(ctx, *jsException, ex); } \ return returnObject; \ } From 2baec5bec66b60a61c598d4338ddcae7806dff44 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 13:09:05 -0700 Subject: [PATCH 09/48] move static methods and constructor --- src/js_realm.hpp | 105 +++++++++++++++++++++++++++++++++++++- src/js_util.hpp | 18 ++++--- src/jsc/jsc_realm.cpp | 114 ++---------------------------------------- 3 files changed, 120 insertions(+), 117 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 0702391b..a04dcb04 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -19,10 +19,12 @@ #pragma once #include "js_util.hpp" +#include "js_schema.hpp" #include "shared_realm.hpp" #include "binding_context.hpp" #include "object_accessor.hpp" #include "results.hpp" +#include "platform.hpp" #include #include @@ -127,6 +129,7 @@ public: using ReturnType = typename T::Return; using ExceptionType = typename T::Exception; + // member methods static void Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static void Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static void Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); @@ -136,6 +139,10 @@ public: static void RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + + // constructor methods + static void Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject); + static void SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static std::string validated_notification_name(JSContextRef ctx, JSValueRef value) { std::string name = RJSValidatedStringForValue(ctx, value); @@ -161,8 +168,104 @@ public: return RJSValidatedStringForValue(ctx, value, "objectType"); } - + static std::string RJSNormalizePath(std::string path) { + if (path.size() && path[0] != '/') { + return default_realm_file_directory() + "/" + path; + } + return path; + } }; + + +template +void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject) { + using RJSAccessor = realm::NativeAccessor; + + realm::Realm::Config config; + std::map defaults; + std::map constructors; + if (argumentCount == 0) { + config.path = default_path(); + } + else if (argumentCount == 1) { + ValueType value = arguments[0]; + if (ValueIsString(ctx, value)) { + config.path = RJSValidatedStringForValue(ctx, value, "path"); + } + else if (ValueIsObject(ctx, value)) { + JSObjectRef object = RJSValidatedValueToObject(ctx, value); + + static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); + JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); + if (!JSValueIsUndefined(ctx, pathValue)) { + config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); + } + else { + config.path = js::default_path(); + } + + static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); + JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); + if (!JSValueIsUndefined(ctx, schemaValue)) { + config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); + } + + static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); + JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); + if (JSValueIsNumber(ctx, versionValue)) { + config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); + } + else { + config.schema_version = 0; + } + + static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); + JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); + if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; + } + } + } + else { + throw std::runtime_error("Invalid arguments when constructing 'Realm'"); + } + + config.path = RJSNormalizePath(config.path); + + ensure_directory_exists_for_file(config.path); + SharedRealm realm = realm::Realm::get_shared_realm(config); + auto delegate = new RealmDelegate(realm, JSContextGetGlobalContext(ctx)); + if (!realm->m_binding_context) { + realm->m_binding_context.reset(delegate); + } + delegate->m_defaults = defaults; + delegate->m_constructors = constructors; + returnObject = WrapObject(ctx, realm_class(), new SharedRealm(realm)); +} + +template +void Realm::SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { + using RJSAccessor = realm::NativeAccessor; + + RJSValidateArgumentRange(argumentCount, 1, 2); + + realm::Realm::Config config; + config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); + if (argumentCount == 2) { + auto encryptionKeyValue = arguments[1]; + std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); + } + + auto version = realm::Realm::get_schema_version(config); + if (version == ObjectStore::NotVersioned) { + RJSSetReturnNumber(ctx, returnObject, -1); + } + else { + RJSSetReturnNumber(ctx, returnObject, version); + } +} template void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { diff --git a/src/js_util.hpp b/src/js_util.hpp index 8c8789ce..3c6222c6 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -33,18 +33,21 @@ #include "js_compat.hpp" #include "schema.hpp" +#define WRAP_EXCEPTION(METHOD, EXCEPTION, ARGS...) \ +try { METHOD(ARGS); } \ +catch(std::exception &e) { RJSSetException(ctx, EXCEPTION, e); } + #define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ JSValueRef returnObject = NULL; \ - try { CLASS_NAME::METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject); } \ - catch(std::exception &ex) { RJSSetException(ctx, *jsException, ex); } \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnObject); \ return returnObject; \ } -#define WRAP_METHOD(METHOD_NAME) \ -JSValueRef METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { \ - JSValueRef returnObject = NULL; \ - METHOD_NAME(ctx, thisObject, argumentCount, arguments, returnObject, *jsException); \ +#define WRAP_CONSTRUCTOR(CLASS_NAME, METHOD_NAME) \ +JSObjectRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ + JSObjectRef returnObject = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, constructor, argumentCount, arguments, returnObject); \ return returnObject; \ } @@ -313,3 +316,4 @@ static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassR return JSValueIsObjectOfClass(ctx, value, jsClass); } + diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp index 258ffe51..7623454c 100644 --- a/src/jsc/jsc_realm.cpp +++ b/src/jsc/jsc_realm.cpp @@ -52,86 +52,6 @@ static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef pro return true; } -inline std::string RJSNormalizePath(std::string path) { - if (path.size() && path[0] != '/') { - return default_realm_file_directory() + "/" + path; - } - return path; -} - -JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Realm::Config config; - std::map defaults; - std::map constructors; - if (argumentCount == 0) { - config.path = js::default_path(); - } - else if (argumentCount == 1) { - JSValueRef value = arguments[0]; - if (JSValueIsString(ctx, value)) { - config.path = RJSValidatedStringForValue(ctx, value, "path"); - } - else if (JSValueIsObject(ctx, value)) { - JSObjectRef object = RJSValidatedValueToObject(ctx, value); - - static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); - JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); - if (!JSValueIsUndefined(ctx, pathValue)) { - config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); - } - else { - config.path = js::default_path(); - } - - static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); - JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); - if (!JSValueIsUndefined(ctx, schemaValue)) { - config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); - } - - static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); - JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); - if (JSValueIsNumber(ctx, versionValue)) { - config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); - } - else { - config.schema_version = 0; - } - - static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); - JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); - if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; - } - } - } - else { - *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); - return NULL; - } - - config.path = RJSNormalizePath(config.path); - - ensure_directory_exists_for_file(config.path); - SharedRealm realm = Realm::get_shared_realm(config); - auto delegate = new js::RealmDelegate(realm, JSContextGetGlobalContext(ctx)); - if (!realm->m_binding_context) { - realm->m_binding_context.reset(delegate); - } - delegate->m_defaults = defaults; - delegate->m_constructors = constructors; - return js::WrapObject(ctx, RJSRealmClass(), new SharedRealm(realm)); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - return NULL; - } -} - bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); } @@ -141,36 +61,13 @@ static const JSStaticValue RealmStaticProperties[] = { {NULL, NULL} }; -template -void RealmSchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { - try { - RJSValidateArgumentRange(argumentCount, 1, 2); - - Realm::Config config; - config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); - if (argumentCount == 2) { - auto encryptionKeyValue = arguments[1]; - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); - } - auto version = Realm::get_schema_version(config); - if (version == ObjectStore::NotVersioned) { - RJSSetReturnNumber(ctx, returnObject, -1); - } - else { - RJSSetReturnNumber(ctx, returnObject, version); - } - } - catch (std::exception &exp) { - RJSSetException(ctx, exceptionObject, exp); - } -} - -WRAP_METHOD(RealmSchemaVersion) +using RJSRealm = realm::js::Realm; +WRAP_CONSTRUCTOR(RJSRealm, Constructor); +WRAP_CLASS_METHOD(RJSRealm, SchemaVersion) static const JSStaticFunction RealmConstructorFuncs[] = { - {"schemaVersion", RealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"schemaVersion", RJSRealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL}, }; @@ -178,7 +75,7 @@ JSClassRef RJSRealmConstructorClass() { JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; realmConstructorDefinition.className = "RealmConstructor"; - realmConstructorDefinition.callAsConstructor = RealmConstructor; + realmConstructorDefinition.callAsConstructor = RJSRealmConstructor; realmConstructorDefinition.hasInstance = RealmHasInstance; realmConstructorDefinition.staticValues = RealmStaticProperties; realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; @@ -198,7 +95,6 @@ JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef pr return NULL; } -using RJSRealm = realm::js::Realm; WRAP_CLASS_METHOD(RJSRealm, Objects) WRAP_CLASS_METHOD(RJSRealm, Create) WRAP_CLASS_METHOD(RJSRealm, Delete) From c249eea505377f88c437c44cf0b653547b82143f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 13:48:24 -0700 Subject: [PATCH 10/48] wrap realm properties --- src/js_realm.hpp | 28 ++++++++++++++++++++++ src/js_util.hpp | 23 ++++++++++++++---- src/jsc/jsc_list.cpp | 24 +++++++++++++++---- src/jsc/jsc_realm.cpp | 56 +++++++++++++------------------------------ 4 files changed, 83 insertions(+), 48 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index a04dcb04..cb369d9f 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -140,9 +140,17 @@ public: static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + // properties + static void GetPath(ContextType ctx, ObjectType object, ReturnType &returnObject); + static void GetSchemaVersion(ContextType ctx, ObjectType object, ReturnType &returnObject); + // constructor methods static void Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject); static void SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); + + // static properties + static void GetDefaultPath(ContextType ctx, ObjectType object, ReturnType &returnObject); + static void SetDefaultPath(ContextType ctx, ObjectType object, ValueType value); static std::string validated_notification_name(JSContextRef ctx, JSValueRef value) { std::string name = RJSValidatedStringForValue(ctx, value); @@ -267,6 +275,26 @@ void Realm::SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argu } } +template +void Realm::GetDefaultPath(ContextType ctx, ObjectType object, ReturnType &returnObject) { + returnObject = RJSValueForString(ctx, realm::js::default_path()); +} + +template +void Realm::SetDefaultPath(ContextType ctx, ObjectType object, ValueType value) { + js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); +} + +template +void Realm::GetPath(ContextType ctx, ObjectType object, ReturnType &returnObject) { + returnObject = RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); +} + +template +void Realm::GetSchemaVersion(ContextType ctx, ObjectType object, ReturnType &returnObject) { + returnObject = JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); +} + template void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { RJSValidateArgumentCount(argumentCount, 1); diff --git a/src/js_util.hpp b/src/js_util.hpp index 3c6222c6..d416827f 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -39,9 +39,9 @@ catch(std::exception &e) { RJSSetException(ctx, EXCEPTION, e); } #define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ - JSValueRef returnObject = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnObject); \ - return returnObject; \ + JSValueRef returnValue = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnValue); \ + return returnValue; \ } #define WRAP_CONSTRUCTOR(CLASS_NAME, METHOD_NAME) \ @@ -51,6 +51,20 @@ JSObjectRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef constructor, return returnObject; \ } +#define WRAP_PROPERTY_GETTER(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ + JSValueRef returnValue = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, returnValue); \ + return returnValue; \ +} + +#define WRAP_PROPERTY_SETTER(CLASS_NAME, METHOD_NAME) \ +bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, value); \ + return true; \ +} + + template inline void RJSFinalize(JSObjectRef object) { delete static_cast(JSObjectGetPrivate(object)); @@ -64,7 +78,7 @@ inline T RJSGetInternal(JSObjectRef jsObject) { template JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL, - JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL) { + JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL, const JSStaticValue *values = NULL) { JSClassDefinition classDefinition = kJSClassDefinitionEmpty; classDefinition.className = name; classDefinition.finalize = RJSFinalize; @@ -73,6 +87,7 @@ JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback classDefinition.staticFunctions = funcs; classDefinition.getPropertyNames = propertyNames; classDefinition.parentClass = parentClass; + classDefinition.staticValues = values; return JSClassCreate(&classDefinition); } diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index 5f4f2802..da2ef3f9 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -24,14 +24,23 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm; +JSValueRef ListGetLength(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { + try { + List *list = RJSGetInternal(object); + return JSValueMakeNumber(ctx, list->size()); + } + catch (std::exception &exp) { + if (exception) { + *exception = RJSMakeError(ctx, exp); + } + return NULL; + } +} + JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { try { List *list = RJSGetInternal(object); std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr == "length") { - return JSValueMakeNumber(ctx, list->size()); - } - return RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(RJSValidatedPositiveIndex(indexStr)))); } catch (std::out_of_range &exp) { @@ -112,8 +121,13 @@ static const JSStaticFunction RJSListFuncs[] = { {NULL, NULL}, }; +static const JSStaticValue RJSListProps[] = { + {"length", ListGetLength, nullptr, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL}, +}; + JSClassRef RJSListClass() { - static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass()); + static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass(), RJSListProps); return s_listClass; } diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp index 7623454c..118bf267 100644 --- a/src/jsc/jsc_realm.cpp +++ b/src/jsc/jsc_realm.cpp @@ -35,36 +35,20 @@ using namespace realm; using RJSAccessor = realm::NativeAccessor; - -static JSValueRef GetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - return RJSValueForString(ctx, realm::js::default_path()); -} - -static bool SetDefaultPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { - try { - js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); - } - catch (std::exception &ex) { - if (jsException) { - *jsException = RJSMakeError(ctx, ex); - } - } - return true; -} - bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); } -static const JSStaticValue RealmStaticProperties[] = { - {"defaultPath", GetDefaultPath, SetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} -}; - - using RJSRealm = realm::js::Realm; WRAP_CONSTRUCTOR(RJSRealm, Constructor); WRAP_CLASS_METHOD(RJSRealm, SchemaVersion) +WRAP_PROPERTY_GETTER(RJSRealm, GetDefaultPath) +WRAP_PROPERTY_SETTER(RJSRealm, SetDefaultPath) + +static const JSStaticValue RealmConstructorStaticProperties[] = { + {"defaultPath", RJSRealmGetDefaultPath, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL} +}; static const JSStaticFunction RealmConstructorFuncs[] = { {"schemaVersion", RJSRealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, @@ -77,24 +61,11 @@ JSClassRef RJSRealmConstructorClass() { realmConstructorDefinition.className = "RealmConstructor"; realmConstructorDefinition.callAsConstructor = RJSRealmConstructor; realmConstructorDefinition.hasInstance = RealmHasInstance; - realmConstructorDefinition.staticValues = RealmStaticProperties; + realmConstructorDefinition.staticValues = RealmConstructorStaticProperties; realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; return JSClassCreate(&realmConstructorDefinition); } -JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - static JSStringRef s_pathString = JSStringCreateWithUTF8CString("path"); - if (JSStringIsEqual(propertyName, s_pathString)) { - return RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); - } - - static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion"); - if (JSStringIsEqual(propertyName, s_schemaVersion)) { - return JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); - } - return NULL; -} - WRAP_CLASS_METHOD(RJSRealm, Objects) WRAP_CLASS_METHOD(RJSRealm, Create) WRAP_CLASS_METHOD(RJSRealm, Delete) @@ -104,6 +75,14 @@ WRAP_CLASS_METHOD(RJSRealm, AddListener) WRAP_CLASS_METHOD(RJSRealm, RemoveListener) WRAP_CLASS_METHOD(RJSRealm, RemoveAllListeners) WRAP_CLASS_METHOD(RJSRealm, Close) +WRAP_PROPERTY_GETTER(RJSRealm, GetPath) +WRAP_PROPERTY_GETTER(RJSRealm, GetSchemaVersion) + +static const JSStaticValue RealmStaticProperties[] = { + {"path", RJSRealmGetPath, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"schemaVersion", RJSRealmGetSchemaVersion, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL} +}; static const JSStaticFunction RJSRealmFuncs[] = { {"objects", RJSRealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, @@ -119,11 +98,10 @@ static const JSStaticFunction RJSRealmFuncs[] = { }; JSClassRef RJSRealmClass() { - static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", RealmGetProperty, NULL, RJSRealmFuncs); + static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", NULL, NULL, RJSRealmFuncs, NULL, NULL, RealmStaticProperties); return s_realmClass; } - namespace realm { namespace js { JSClassRef realm_class() { return RJSRealmClass(); }; From 29213f1d8797dfb02610d9503edb395d5151c206 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 14:18:44 -0700 Subject: [PATCH 11/48] wrap list properties --- src/js_list.hpp | 22 +++++++++++++++ src/js_util.hpp | 25 +++++++++++++++++ src/jsc/jsc_list.cpp | 65 ++++---------------------------------------- 3 files changed, 52 insertions(+), 60 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index dbfdaf83..9bf07f48 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -41,6 +41,10 @@ struct List { using ObjectType = typename T::Object; using ValueType = typename T::Value; using ReturnType = typename T::Return; + + static void GetLength(ContextType ctx, ObjectType thisObject, ReturnType &ret); + static void GetIndex(ContextType ctx, ObjectType thisObject, size_t index, ReturnType &ret); + static void SetIndex(ContextType ctx, ObjectType thisObject, size_t index, ValueType value); static void Push(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); static void Pop(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); @@ -51,6 +55,24 @@ struct List { static void Filtered(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); static void Sorted(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); }; + +template +void List::GetLength(ContextType ctx, ObjectType object, ReturnType &ret) { + realm::List *list = RJSGetInternal(object); + RJSSetReturnNumber(ctx, ret, list->size()); +} + +template +void List::GetIndex(ContextType ctx, ObjectType object, size_t index, ReturnType &ret) { + realm::List *list = RJSGetInternal(object); + ret = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); +} + +template +void List::SetIndex(ContextType ctx, ObjectType object, size_t index, ValueType value) { + realm::List *list = RJSGetInternal(object); + list->set(ctx, value, index); +} template void List::Push(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { diff --git a/src/js_util.hpp b/src/js_util.hpp index d416827f..fd6beba1 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -64,6 +64,31 @@ bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef return true; \ } +// for stol failure (std::invalid_argument) this could be another property that is handled externally, so ignore +#define WRAP_INDEXED_GETTER(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ + JSValueRef returnValue = NULL; \ + try { \ + size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ + CLASS_NAME::METHOD_NAME(ctx, object, index, returnValue); return returnValue; \ + } \ + catch (std::out_of_range &exp) { return JSValueMakeUndefined(ctx); } \ + catch (std::invalid_argument &exp) { return NULL; } \ + catch (std::exception &e) { RJSSetException(ctx, *ex, e); return NULL; } \ +} + +#define WRAP_INDEXED_SETTER(CLASS_NAME, METHOD_NAME) \ +bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ + try { \ + size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ + { CLASS_NAME::METHOD_NAME(ctx, object, index, value); return true; } \ + } \ + catch (std::out_of_range &exp) { RJSSetException(ctx, *ex, exp); } \ + catch (std::invalid_argument &exp) { *ex = RJSMakeError(ctx, "Invalid index"); } \ + catch (std::exception &e) { RJSSetException(ctx, *ex, e); } \ + return false; \ +} + template inline void RJSFinalize(JSObjectRef object) { diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index da2ef3f9..347c8688 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -24,64 +24,6 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm; -JSValueRef ListGetLength(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - try { - List *list = RJSGetInternal(object); - return JSValueMakeNumber(ctx, list->size()); - } - catch (std::exception &exp) { - if (exception) { - *exception = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - try { - List *list = RJSGetInternal(object); - std::string indexStr = RJSStringForJSString(propertyName); - return RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(RJSValidatedPositiveIndex(indexStr)))); - } - catch (std::out_of_range &exp) { - // getters for nonexistent properties in JS should always return undefined - return JSValueMakeUndefined(ctx); - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -bool ListSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* jsException) { - try { - List *list = RJSGetInternal(object); - std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr == "length") { - throw std::runtime_error("The 'length' property is readonly."); - } - - list->set(ctx, value, RJSValidatedPositiveIndex(indexStr)); - return true; - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - return false; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return false; - } -} - void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { List *list = RJSGetInternal(object); size_t size = list->size(); @@ -96,6 +38,9 @@ void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccum } using RJSList = realm::js::List; +WRAP_PROPERTY_GETTER(RJSList, GetLength) +WRAP_INDEXED_GETTER(RJSList, GetIndex) +WRAP_INDEXED_SETTER(RJSList, SetIndex) WRAP_CLASS_METHOD(RJSList, Push) WRAP_CLASS_METHOD(RJSList, Pop) WRAP_CLASS_METHOD(RJSList, Unshift) @@ -122,12 +67,12 @@ static const JSStaticFunction RJSListFuncs[] = { }; static const JSStaticValue RJSListProps[] = { - {"length", ListGetLength, nullptr, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"length", RJSListGetLength, nullptr, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL}, }; JSClassRef RJSListClass() { - static JSClassRef s_listClass = RJSCreateWrapperClass("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass(), RJSListProps); + static JSClassRef s_listClass = RJSCreateWrapperClass("List", RJSListGetIndex, RJSListSetIndex, RJSListFuncs, ListPropertyNames, RJSCollectionClass(), RJSListProps); return s_listClass; } From 61685dee837d72b20e47e6dc37adb65d1577bb60 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 14:56:33 -0700 Subject: [PATCH 12/48] use string wrapper, finish realm constructor conversion --- src/js_realm.hpp | 23 ++++++++++++----------- src/jsc/types.hpp | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index cb369d9f..d7d76ede 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -188,7 +188,8 @@ public: template void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject) { using RJSAccessor = realm::NativeAccessor; - + using StringType = typename T::String; + realm::Realm::Config config; std::map defaults; std::map constructors; @@ -201,10 +202,10 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum config.path = RJSValidatedStringForValue(ctx, value, "path"); } else if (ValueIsObject(ctx, value)) { - JSObjectRef object = RJSValidatedValueToObject(ctx, value); + ObjectType object = RJSValidatedValueToObject(ctx, value); - static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); - JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); + StringType pathString("path"); + ValueType pathValue = RJSValidatedPropertyValue(ctx, object, pathString); if (!JSValueIsUndefined(ctx, pathValue)) { config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); } @@ -212,14 +213,14 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum config.path = js::default_path(); } - static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); - JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); + StringType schemaString("schema"); + ValueType schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); if (!JSValueIsUndefined(ctx, schemaValue)) { config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); } - static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); - JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); + StringType schemaVersionString("schemaVersion"); + ValueType versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); if (JSValueIsNumber(ctx, versionValue)) { config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); } @@ -227,8 +228,8 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum config.schema_version = 0; } - static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey"); - JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); + StringType encryptionKeyString("encryptionKey"); + ValueType encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; @@ -254,7 +255,7 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum template void Realm::SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - using RJSAccessor = realm::NativeAccessor; + using RJSAccessor = realm::NativeAccessor; RJSValidateArgumentRange(argumentCount, 1, 2); diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp index ffb8076a..7575e2a6 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/types.hpp @@ -24,14 +24,24 @@ namespace realm { namespace jsc { - + +class String { + public: + String(const char * str) : m_str(JSStringCreateWithUTF8CString(str)) {} + ~String() { JSStringRelease(m_str); } + operator JSStringRef() const { return m_str; } + + private: + JSStringRef m_str; +}; + struct Types { using Context = JSContextRef; using GlobalContext = JSGlobalContextRef; using ObjectClass = JSClassRef; using Value = JSValueRef; using Object = JSObjectRef; - using String = JSStringRef; + using String = jsc::String; using Function = JSObjectRef; using Return = JSValueRef; using Exception = JSValueRef; From f80bcd882d8b1f44d929210d0fee382d87c9cd7d Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 30 Mar 2016 16:14:48 -0700 Subject: [PATCH 13/48] convert schema parsing --- src/js_realm.hpp | 10 ++- src/js_schema.cpp | 160 ------------------------------------- src/js_schema.hpp | 179 +++++++++++++++++++++++++++++++++++++++++- src/js_util.hpp | 18 ++--- src/jsc/js_compat.hpp | 24 +++++- src/jsc/types.hpp | 1 + 6 files changed, 216 insertions(+), 176 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index d7d76ede..848ade78 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -20,11 +20,13 @@ #include "js_util.hpp" #include "js_schema.hpp" + #include "shared_realm.hpp" #include "binding_context.hpp" #include "object_accessor.hpp" #include "results.hpp" #include "platform.hpp" + #include #include @@ -191,8 +193,8 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum using StringType = typename T::String; realm::Realm::Config config; - std::map defaults; - std::map constructors; + typename Schema::ObjectDefaultsMap defaults; + typename Schema::ConstructorMap constructors; if (argumentCount == 0) { config.path = default_path(); } @@ -215,8 +217,8 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum StringType schemaString("schema"); ValueType schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); - if (!JSValueIsUndefined(ctx, schemaValue)) { - config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); + if (!ValueIsUndefined(ctx, schemaValue)) { + config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); } StringType schemaVersionString("schemaVersion"); diff --git a/src/js_schema.cpp b/src/js_schema.cpp index e803fcc9..ff78e108 100644 --- a/src/js_schema.cpp +++ b/src/js_schema.cpp @@ -44,163 +44,3 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { wrapper->owned = false; return js::WrapObject(ctx, RJSSchemaClass(), wrapper); } - -static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) { - static JSStringRef defaultString = JSStringCreateWithUTF8CString("default"); - static JSStringRef indexedString = JSStringCreateWithUTF8CString("indexed"); - static JSStringRef typeString = JSStringCreateWithUTF8CString("type"); - static JSStringRef objectTypeString = JSStringCreateWithUTF8CString("objectType"); - static JSStringRef optionalString = JSStringCreateWithUTF8CString("optional"); - - Property prop; - prop.name = propertyName; - - JSObjectRef propertyObject = NULL; - std::string type; - - if (JSValueIsObject(ctx, propertyAttributes)) { - propertyObject = RJSValidatedValueToObject(ctx, propertyAttributes); - type = RJSValidatedStringProperty(ctx, propertyObject, typeString); - - JSValueRef optionalValue = JSObjectGetProperty(ctx, propertyObject, optionalString, NULL); - if (!JSValueIsUndefined(ctx, optionalValue)) { - if (!JSValueIsBoolean(ctx, optionalValue)) { - throw std::runtime_error("'optional' designation expected to be of type boolean"); - } - prop.is_nullable = JSValueToBoolean(ctx, optionalValue); - } - } - else { - type = RJSValidatedStringForValue(ctx, propertyAttributes); - } - - if (type == "bool") { - prop.type = PropertyTypeBool; - } - else if (type == "int") { - prop.type = PropertyTypeInt; - } - else if (type == "float") { - prop.type = PropertyTypeFloat; - } - else if (type == "double") { - prop.type = PropertyTypeDouble; - } - else if (type == "string") { - prop.type = PropertyTypeString; - } - else if (type == "date") { - prop.type = PropertyTypeDate; - } - else if (type == "data") { - prop.type = PropertyTypeData; - } - else if (type == "list") { - if (!propertyObject) { - throw std::runtime_error("List property must specify 'objectType'"); - } - prop.type = PropertyTypeArray; - prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); - } - else { - prop.type = PropertyTypeObject; - prop.is_nullable = true; - - // The type could either be 'object' or the name of another object type in the same schema. - if (type == "object") { - if (!propertyObject) { - throw std::runtime_error("Object property must specify 'objectType'"); - } - prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); - } - else { - prop.object_type = type; - } - } - - if (propertyObject) { - JSValueRef defaultValue = RJSValidatedPropertyValue(ctx, propertyObject, defaultString); - if (!JSValueIsUndefined(ctx, defaultValue)) { - JSValueProtect(ctx, defaultValue); - objectDefaults.emplace(prop.name, defaultValue); - } - - JSValueRef indexedValue = RJSValidatedPropertyValue(ctx, propertyObject, indexedString); - if (!JSValueIsUndefined(ctx, indexedValue)) { - prop.is_indexed = JSValueToBoolean(ctx, indexedValue); - } - } - - return prop; -} - -static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject, std::map &defaults, std::map &constructors) { - static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); - static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey"); - static JSStringRef propertiesString = JSStringCreateWithUTF8CString("properties"); - static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); - - JSObjectRef objectConstructor = NULL; - - if (JSObjectIsConstructor(ctx, objectSchemaObject)) { - objectConstructor = objectSchemaObject; - objectSchemaObject = RJSValidatedObjectProperty(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); - } - - ObjectDefaults objectDefaults; - ObjectSchema objectSchema; - objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); - - JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); - if (RJSIsValueArray(ctx, propertiesObject)) { - size_t propertyCount = RJSValidatedListLength(ctx, propertiesObject); - for (size_t i = 0; i < propertyCount; i++) { - JSObjectRef propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i); - std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString); - objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyObject, propertyName, objectDefaults)); - } - } - else { - JSPropertyNameArrayRef propertyNames = JSObjectCopyPropertyNames(ctx, propertiesObject); - size_t propertyCount = JSPropertyNameArrayGetCount(propertyNames); - for (size_t i = 0; i < propertyCount; i++) { - JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNames, i); - JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, propertyName); - objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyValue, RJSStringForJSString(propertyName), objectDefaults)); - } - JSPropertyNameArrayRelease(propertyNames); - } - - JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); - if (!JSValueIsUndefined(ctx, primaryValue)) { - objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue); - Property *property = objectSchema.primary_key_property(); - if (!property) { - throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'"); - } - property->is_primary = true; - } - - // Store prototype so that objects of this type will have their prototype set to this prototype object. - if (objectConstructor) { - JSValueProtect(ctx, objectConstructor); - constructors[objectSchema.name] = std::move(objectConstructor); - } - - defaults.emplace(objectSchema.name, std::move(objectDefaults)); - - return objectSchema; -} - -realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map &defaults, std::map &constructors) { - std::vector schema; - size_t length = RJSValidatedListLength(ctx, jsonObject); - for (unsigned int i = 0; i < length; i++) { - JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i); - ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema, defaults, constructors); - schema.emplace_back(std::move(objectSchema)); - } - - return Schema(schema); -} - diff --git a/src/js_schema.hpp b/src/js_schema.hpp index 518e432a..b6b6f627 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -19,14 +19,189 @@ #pragma once #include "js_util.hpp" +#include "schema.hpp" #include namespace realm { class Schema; - using ObjectDefaults = std::map; } JSClassRef RJSSchemaClass(); JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema); -realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map &defaults, std::map &constructors); +namespace realm { +namespace js { + +template +struct Schema +{ + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using ReturnType = typename T::Return; + using StringType = typename T::String; + using ObjectDefaults = std::map; + using ObjectDefaultsMap = std::map; + using ConstructorMap = std::map; + + static Property parse_property(ContextType ctx, ValueType attributes, std::string propertyame, ObjectDefaults &objectDefaults); + static ObjectSchema parse_object_schema(ContextType ctx, ObjectType objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors); + static realm::Schema parse_schema(ContextType ctx, ObjectType jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors); +}; + +template +Property Schema::parse_property(ContextType ctx, ValueType attributes, std::string propertyName, ObjectDefaults &objectDefaults) { + StringType defaultString("default"); + StringType indexedString("indexed"); + StringType typeString("type"); + StringType objectTypeString("objectType"); + StringType optionalString("optional"); + + Property prop; + prop.name = propertyName; + + JSObjectRef propertyObject = NULL; + std::string type; + + if (ValueIsObject(ctx, attributes)) { + propertyObject = RJSValidatedValueToObject(ctx, attributes); + type = RJSValidatedStringProperty(ctx, propertyObject, typeString); + + ValueType optionalValue = ObjectGetProperty(ctx, propertyObject, optionalString, NULL); + if (!ValueIsUndefined(ctx, optionalValue)) { + prop.is_nullable = RJSValidatedValueToBoolean(ctx, optionalValue, "'optional' designation expected to be of type boolean"); + } + } + else { + type = RJSValidatedStringForValue(ctx, attributes); + } + + if (type == "bool") { + prop.type = PropertyTypeBool; + } + else if (type == "int") { + prop.type = PropertyTypeInt; + } + else if (type == "float") { + prop.type = PropertyTypeFloat; + } + else if (type == "double") { + prop.type = PropertyTypeDouble; + } + else if (type == "string") { + prop.type = PropertyTypeString; + } + else if (type == "date") { + prop.type = PropertyTypeDate; + } + else if (type == "data") { + prop.type = PropertyTypeData; + } + else if (type == "list") { + if (!propertyObject) { + throw std::runtime_error("List property must specify 'objectType'"); + } + prop.type = PropertyTypeArray; + prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); + } + else { + prop.type = PropertyTypeObject; + prop.is_nullable = true; + + // The type could either be 'object' or the name of another object type in the same schema. + if (type == "object") { + if (!propertyObject) { + throw std::runtime_error("Object property must specify 'objectType'"); + } + prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); + } + else { + prop.object_type = type; + } + } + + if (propertyObject) { + ValueType defaultValue = RJSValidatedPropertyValue(ctx, propertyObject, defaultString); + if (!ValueIsUndefined(ctx, defaultValue)) { + ValueProtect(ctx, defaultValue); + objectDefaults.emplace(prop.name, defaultValue); + } + + ValueType indexedValue = RJSValidatedPropertyValue(ctx, propertyObject, indexedString); + if (!ValueIsUndefined(ctx, indexedValue)) { + prop.is_indexed = RJSValidatedValueToBoolean(ctx, indexedValue); + } + } + + return prop; +} + +template +ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { + StringType nameString("name"); + StringType primaryString("primaryKey"); + StringType propertiesString("properties"); + StringType schemaString("schema"); + + ObjectType objectConstructor = NULL; + if (ValueIsConstructor(ctx, objectSchemaObject)) { + objectConstructor = objectSchemaObject; + objectSchemaObject = RJSValidatedObjectProperty(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); + } + + ObjectDefaults objectDefaults; + ObjectSchema objectSchema; + objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); + + ObjectType propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); + if (RJSIsValueArray(ctx, propertiesObject)) { + size_t propertyCount = RJSValidatedListLength(ctx, propertiesObject); + for (size_t i = 0; i < propertyCount; i++) { + ObjectType propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i); + std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString); + objectSchema.properties.emplace_back(parse_property(ctx, propertyObject, propertyName, objectDefaults)); + } + } + else { + auto propertyNames = ObjectGetPropertyNames(ctx, propertiesObject); + for (auto propertyName : propertyNames) { + ValueType propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, StringType(propertyName.c_str())); + objectSchema.properties.emplace_back(parse_property(ctx, propertyValue, propertyName, objectDefaults)); + } + } + + JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); + if (!JSValueIsUndefined(ctx, primaryValue)) { + objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue); + Property *property = objectSchema.primary_key_property(); + if (!property) { + throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'"); + } + property->is_primary = true; + } + + // Store prototype so that objects of this type will have their prototype set to this prototype object. + if (objectConstructor) { + ValueProtect(ctx, objectConstructor); + constructors[objectSchema.name] = std::move(objectConstructor); + } + + defaults.emplace(objectSchema.name, std::move(objectDefaults)); + + return objectSchema; +} + +template +realm::Schema Schema::parse_schema(ContextType ctx, ObjectType jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { + std::vector schema; + size_t length = RJSValidatedListLength(ctx, jsonObject); + for (unsigned int i = 0; i < length; i++) { + JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i); + ObjectSchema objectSchema = parse_object_schema(ctx, jsonObjectSchema, defaults, constructors); + schema.emplace_back(std::move(objectSchema)); + } + return realm::Schema(schema); +} + +} +} \ No newline at end of file diff --git a/src/js_util.hpp b/src/js_util.hpp index fd6beba1..ce7d1388 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -123,19 +123,19 @@ std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const JSStringRef RJSStringForString(const std::string &str); JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str); -inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected, const char *message = NULL) { +inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected, const char *message = nullptr) { if (argumentCount != expected) { throw std::invalid_argument(message ?: "Invalid arguments"); } } -inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected, const char *message = NULL) { +inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected, const char *message = nullptr) { if (argumentCount < expected) { throw std::invalid_argument(message ?: "Invalid arguments"); } } -inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max, const char *message = NULL) { +inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max, const char *message = nullptr) { if (argumentCount < min || argumentCount > max) { throw std::invalid_argument(message ?: "Invalid arguments"); } @@ -159,7 +159,7 @@ bool RJSIsValueArray(JSContextRef ctx, JSValueRef value); bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value); bool RJSIsValueDate(JSContextRef ctx, JSValueRef value); -static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object) { throw std::runtime_error(message ?: "Value is not an object."); @@ -167,7 +167,7 @@ static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef return object; } -static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object || !RJSIsValueDate(ctx, object)) { throw std::runtime_error(message ?: "Value is not a date."); @@ -175,7 +175,7 @@ static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef v return object; } -static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object || !JSObjectIsFunction(ctx, object)) { throw std::runtime_error(message ?: "Value is not a function."); @@ -199,9 +199,9 @@ static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef valu return number; } -static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value) { +static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value, const char *err = nullptr) { if (!JSValueIsBoolean(ctx, value)) { - throw std::invalid_argument("Value is not a boolean."); + throw std::invalid_argument(err ?: "Value is not a boolean."); } return JSValueToBoolean(ctx, value); } @@ -224,7 +224,7 @@ static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectR return propertyValue; } -static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) { +static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = nullptr) { JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); if (JSValueIsUndefined(ctx, propertyValue)) { throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined"); diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp index 0cd54352..ecdd5e6a 100644 --- a/src/jsc/js_compat.hpp +++ b/src/jsc/js_compat.hpp @@ -20,6 +20,7 @@ #include "types.hpp" #include +#include namespace realm { namespace js { @@ -33,10 +34,31 @@ static inline bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value valu static inline bool ValueIsConstructor(jsc::Types::Context ctx, jsc::Types::Value value) { return ValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); } static inline jsc::Types::Object ValueToObject(jsc::Types::Context ctx, jsc::Types::Value value) { return (JSObjectRef)value; } - + static inline void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } static inline void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } +static inline std::string StringTypeToString(JSStringRef jsString) { + std::string str; + size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString); + str.resize(maxSize); + str.resize(JSStringGetUTF8CString(jsString, &str[0], maxSize) - 1); + return str; +} + +static inline std::vector ObjectGetPropertyNames(jsc::Types::Context ctx, jsc::Types::Object object) { + JSPropertyNameArrayRef propertyNames = JSObjectCopyPropertyNames(ctx, object); + size_t propertyCount = JSPropertyNameArrayGetCount(propertyNames); + std::vector outNames; + for (size_t i = 0; i < propertyCount; i++) { + outNames.push_back(StringTypeToString(JSPropertyNameArrayGetNameAtIndex(propertyNames, i))); + } + JSPropertyNameArrayRelease(propertyNames); + return outNames; +} +static inline jsc::Types::Value ObjectGetProperty(jsc::Types::Context ctx, jsc::Types::Object object, jsc::Types::String propertyName, jsc::Types::Exception *exception) { + return JSObjectGetProperty(ctx, object, propertyName, exception); +} static inline void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { JSObjectCallAsFunction(ctx, function, thisObject, argsCount, args, &exception); } diff --git a/src/jsc/types.hpp b/src/jsc/types.hpp index 7575e2a6..829e2953 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/types.hpp @@ -28,6 +28,7 @@ namespace jsc { class String { public: String(const char * str) : m_str(JSStringCreateWithUTF8CString(str)) {} + String(const String &other) : m_str(JSStringRetain(other)) {} ~String() { JSStringRelease(m_str); } operator JSStringRef() const { return m_str; } From 05c432deb1dfeac22db54e58d5724238affa6663 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Wed, 30 Mar 2016 14:11:57 -0700 Subject: [PATCH 14/48] WIP Node support The JS engine details are mostly abstracted away. This breaks JSC support until the rest of the pieces are in place. The Node version builds and runs, but crashes when creating a Realm object. --- binding.gyp | 43 ++ package.json | 3 + src/ios/RealmJS.h | 2 +- src/ios/RealmJS.xcodeproj/project.pbxproj | 343 ++++++++++-- src/js_class.hpp | 100 ++++ src/js_list.hpp | 222 +++++--- src/js_object.cpp | 295 ---------- src/js_object.hpp | 98 +++- src/js_object_accessor.hpp | 145 +++++ src/js_realm.cpp | 4 +- src/js_realm.hpp | 412 +++++++------- src/js_results.cpp | 13 +- src/js_results.hpp | 5 +- src/js_schema.cpp | 2 + src/js_schema.hpp | 161 +++--- src/js_types.hpp | 294 ++++++++++ src/js_util.cpp | 99 ---- src/js_util.hpp | 340 ++---------- src/jsc/js_compat.hpp | 83 --- src/jsc/jsc_class.hpp | 309 +++++++++++ .../jsc_collection.cpp} | 2 +- .../jsc_collection.hpp} | 2 +- src/{js_init.cpp => jsc/jsc_init.cpp} | 56 +- src/{js_init.h => jsc/jsc_init.h} | 0 src/jsc/jsc_init.hpp | 22 + src/jsc/jsc_list.cpp | 7 +- src/jsc/jsc_list.hpp | 2 +- src/jsc/jsc_object_accessor.cpp | 90 ++++ .../{types.hpp => jsc_object_accessor.hpp} | 35 +- src/jsc/jsc_realm.cpp | 6 +- src/jsc/jsc_realm.hpp | 2 +- src/jsc/jsc_types.hpp | 502 ++++++++++++++++++ src/jsc/jsc_util.hpp | 134 +++-- src/node/node_class.hpp | 278 ++++++++++ src/node/node_dummy.c | 19 + src/node/node_init.cpp | 39 ++ src/node/node_init.hpp | 21 + src/node/node_object_accessor.cpp | 36 ++ src/node/node_object_accessor.hpp | 30 ++ src/node/node_types.hpp | 473 +++++++++++++++++ src/rpc.cpp | 2 +- 41 files changed, 3412 insertions(+), 1319 deletions(-) create mode 100644 binding.gyp create mode 100644 src/js_class.hpp delete mode 100644 src/js_object.cpp create mode 100644 src/js_object_accessor.hpp create mode 100644 src/js_types.hpp delete mode 100644 src/js_util.cpp delete mode 100644 src/jsc/js_compat.hpp create mode 100644 src/jsc/jsc_class.hpp rename src/{js_collection.cpp => jsc/jsc_collection.cpp} (97%) rename src/{js_collection.hpp => jsc/jsc_collection.hpp} (96%) rename src/{js_init.cpp => jsc/jsc_init.cpp} (66%) rename src/{js_init.h => jsc/jsc_init.h} (100%) create mode 100644 src/jsc/jsc_init.hpp create mode 100644 src/jsc/jsc_object_accessor.cpp rename src/jsc/{types.hpp => jsc_object_accessor.hpp} (50%) create mode 100644 src/jsc/jsc_types.hpp create mode 100644 src/node/node_class.hpp create mode 100644 src/node/node_dummy.c create mode 100644 src/node/node_init.cpp create mode 100644 src/node/node_init.hpp create mode 100644 src/node/node_object_accessor.cpp create mode 100644 src/node/node_object_accessor.hpp create mode 100644 src/node/node_types.hpp diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 00000000..a259f902 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,43 @@ +{ + "targets": [ + { + "target_name": "realm", + "sources": [ + "src/js_realm.cpp", + "src/node/node_init.cpp", + "src/node/node_object_accessor.cpp", + "src/object-store/src/index_set.cpp", + "src/object-store/src/list.cpp", + "src/object-store/src/object_schema.cpp", + "src/object-store/src/object_store.cpp", + "src/object-store/src/results.cpp", + "src/object-store/src/schema.cpp", + "src/object-store/src/shared_realm.cpp", + "src/object-store/src/impl/async_query.cpp", + "src/object-store/src/impl/transact_log_handler.cpp", + "src/object-store/src/impl/realm_coordinator.cpp", + "src/object-store/src/impl/apple/external_commit_helper.cpp", + "src/object-store/src/impl/apple/weak_realm_notifier.cpp", + "src/object-store/src/parser/parser.cpp", + "src/object-store/src/parser/query_builder.cpp", + "src/ios/platform.mm" + ], + "include_dirs": [ + "core/include", + "node_modules/nan", + "src", + "src/object-store/src", + "src/object-store/src/impl", + "src/object-store/src/impl/apple", + "src/object-store/src/parser", + "src/object-store/external/pegtl" + ], + "cflags_cc": ["-DREALM_HAVE_CONFIG", "-fexceptions", "-frtti", "-std=c++14", "-g", "-O0"], + "ldflags": ["-Lcore", "-lrealm"], + "xcode_settings": { + "OTHER_CFLAGS": ["-mmacosx-version-min=10.8", "-DREALM_HAVE_CONFIG", "-fexceptions", "-frtti", "-std=c++14", "-stdlib=libc++", "-g", "-O0", "-Wno-mismatched-tags"], + "OTHER_LDFLAGS": ["-mmacosx-version-min=10.8", "-framework", "Foundation", "-Lcore", "-lrealm", "-std=c++14"] + } + } + ] +} diff --git a/package.json b/package.json index 8ff2303b..6839cfa7 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,9 @@ "prepublish": "scripts/prepublish.sh" }, "dependencies": { + "bindings": "^1.2.1", + "nan": "^2.2.1", + "node-gyp": "^3.3.1", "rnpm": "1.5.2", "xcode": "0.8.4" }, diff --git a/src/ios/RealmJS.h b/src/ios/RealmJS.h index fd67f48c..1649a416 100644 --- a/src/ios/RealmJS.h +++ b/src/ios/RealmJS.h @@ -19,6 +19,6 @@ #ifndef REALM_JS_H #define REALM_JS_H -#include +#include #endif /* REALM_JS_H */ diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 60941658..7602ddf3 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -11,13 +11,13 @@ 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 025678961CAB478800FB8501 /* jsc_realm.cpp */; }; 0270BC821B7D020100010E03 /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */; }; 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; - 029048121C0428DF00ABDED4 /* js_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* js_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 029048121C0428DF00ABDED4 /* jsc_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* jsc_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; 029048141C0428DF00ABDED4 /* js_list.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048041C0428DF00ABDED4 /* js_list.hpp */; }; 029048161C0428DF00ABDED4 /* js_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048061C0428DF00ABDED4 /* js_object.hpp */; }; 029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048081C0428DF00ABDED4 /* js_realm.hpp */; }; 0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480A1C0428DF00ABDED4 /* js_results.hpp */; }; 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480C1C0428DF00ABDED4 /* js_schema.hpp */; }; - 0290481E1C0428DF00ABDED4 /* js_util.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480E1C0428DF00ABDED4 /* js_util.hpp */; }; + 0290481E1C0428DF00ABDED4 /* jsc_util.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */; }; 029048201C0428DF00ABDED4 /* rpc.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048101C0428DF00ABDED4 /* rpc.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; 029048371C042A3C00ABDED4 /* platform.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048351C042A3C00ABDED4 /* platform.hpp */; }; 0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0290483A1C042EE200ABDED4 /* RealmJS.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -38,13 +38,33 @@ 02F59EE11C88F2BB007F774C /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; }; 02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; }; 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; }; + F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; }; + F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; }; + F60102D51CBB96AE00EC01BA /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB11C88F17D007F774C /* list.cpp */; }; + F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB41C88F17D007F774C /* object_schema.cpp */; }; + F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB61C88F17D007F774C /* object_store.cpp */; }; + F60102D81CBB96BD00EC01BA /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB91C88F17D007F774C /* results.cpp */; }; + F60102D91CBB96C100EC01BA /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBB1C88F17D007F774C /* schema.cpp */; }; + F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBD1C88F17D007F774C /* shared_realm.cpp */; }; + F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC61C88F190007F774C /* parser.cpp */; }; + F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC81C88F190007F774C /* query_builder.cpp */; }; + F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */; }; + F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */; }; + F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; }; + F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; }; + F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; }; + F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */; }; + F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; }; + F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */; }; + F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; }; + F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.c */; }; F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; }; - F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* js_init.cpp */; }; - F63FF2C81C12469E00B3B8E0 /* js_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048051C0428DF00ABDED4 /* js_object.cpp */; }; + F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; }; + F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; }; + F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; }; F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; }; F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048091C0428DF00ABDED4 /* js_results.cpp */; }; F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480B1C0428DF00ABDED4 /* js_schema.cpp */; }; - F63FF2CC1C12469E00B3B8E0 /* js_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480D1C0428DF00ABDED4 /* js_util.cpp */; }; F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; }; F63FF2DE1C1565EA00B3B8E0 /* RealmJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */; }; F63FF2E11C1591EC00B3B8E0 /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; }; @@ -66,8 +86,8 @@ F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; }; F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6BB7DF01BF681BC00D0A69E /* base64.hpp */; }; F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; }; - F6CB31001C8EDDAB0070EF3F /* js_collection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */; }; - F6CB31011C8EDDBB0070EF3F /* js_collection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */; }; + F6CB31001C8EDDAB0070EF3F /* jsc_collection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */; }; + F6CB31011C8EDDBB0070EF3F /* jsc_collection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -101,17 +121,16 @@ /* Begin PBXFileReference section */ 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = ""; }; - 025678951CAB392000FB8501 /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = jsc/types.hpp; sourceTree = ""; }; - 025678961CAB478800FB8501 /* jsc_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_realm.cpp; path = jsc/jsc_realm.cpp; sourceTree = ""; }; - 025678971CAB478800FB8501 /* jsc_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_realm.hpp; path = jsc/jsc_realm.hpp; sourceTree = ""; }; + 025678951CAB392000FB8501 /* jsc_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_types.hpp; sourceTree = ""; }; + 025678961CAB478800FB8501 /* jsc_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_realm.cpp; sourceTree = ""; }; + 025678971CAB478800FB8501 /* jsc_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_realm.hpp; sourceTree = ""; }; 0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = ""; }; 0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = ""; }; 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ios/RealmJSTests.mm; sourceTree = ""; }; - 029048011C0428DF00ABDED4 /* js_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_init.cpp; sourceTree = ""; }; - 029048021C0428DF00ABDED4 /* js_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = js_init.h; sourceTree = ""; }; + 029048011C0428DF00ABDED4 /* jsc_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_init.cpp; sourceTree = ""; }; + 029048021C0428DF00ABDED4 /* jsc_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsc_init.h; sourceTree = ""; }; 029048041C0428DF00ABDED4 /* js_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_list.hpp; sourceTree = ""; }; - 029048051C0428DF00ABDED4 /* js_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_object.cpp; sourceTree = ""; }; 029048061C0428DF00ABDED4 /* js_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_object.hpp; sourceTree = ""; }; 029048071C0428DF00ABDED4 /* js_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_realm.cpp; sourceTree = ""; }; 029048081C0428DF00ABDED4 /* js_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm.hpp; sourceTree = ""; }; @@ -119,17 +138,15 @@ 0290480A1C0428DF00ABDED4 /* js_results.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_results.hpp; sourceTree = ""; }; 0290480B1C0428DF00ABDED4 /* js_schema.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_schema.cpp; sourceTree = ""; }; 0290480C1C0428DF00ABDED4 /* js_schema.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_schema.hpp; sourceTree = ""; }; - 0290480D1C0428DF00ABDED4 /* js_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_util.cpp; sourceTree = ""; }; - 0290480E1C0428DF00ABDED4 /* js_util.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = ""; }; + 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_util.hpp; sourceTree = ""; }; 0290480F1C0428DF00ABDED4 /* rpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rpc.cpp; sourceTree = ""; }; 029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = ""; }; 029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = ""; }; 029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = ""; }; 0290483A1C042EE200ABDED4 /* RealmJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmJS.h; sourceTree = ""; }; 02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; - 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_list.cpp; path = jsc/jsc_list.cpp; sourceTree = ""; }; - 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_list.hpp; path = jsc/jsc_list.hpp; sourceTree = ""; }; - 02AFE5B11CAB133500953DA3 /* js_compat.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = js_compat.hpp; path = jsc/js_compat.hpp; sourceTree = ""; }; + 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_list.cpp; sourceTree = ""; }; + 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_list.hpp; sourceTree = ""; }; 02B58CB11AE99CEC009B348C /* RealmJS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmJS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -167,7 +184,22 @@ 02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = transact_log_handler.hpp; path = src/impl/transact_log_handler.hpp; sourceTree = ""; }; 02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier_base.hpp; path = src/impl/weak_realm_notifier_base.hpp; sourceTree = ""; }; 02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier.hpp; path = src/impl/weak_realm_notifier.hpp; sourceTree = ""; }; + F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = ""; }; + F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = ""; }; + F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_object_accessor.cpp; sourceTree = ""; }; + F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = ""; }; + F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = ""; }; + F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = ""; }; F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; + F620F0521CAF0B600082977B /* js_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_class.hpp; sourceTree = ""; }; + F620F0531CAF2EF70082977B /* jsc_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_class.hpp; sourceTree = ""; }; + F620F0551CB655A50082977B /* node_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_class.hpp; sourceTree = ""; }; + F620F0571CB766DA0082977B /* node_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_init.cpp; sourceTree = ""; }; + F620F0591CB7B4C80082977B /* js_object_accessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_object_accessor.hpp; sourceTree = ""; }; + F620F0741CB9F60C0082977B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; }; + F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = ""; }; + F6267BCA1CADC49200AC36B1 /* node_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node_dummy.c; sourceTree = ""; }; + F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; }; F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RealmJS.mm; sourceTree = ""; }; F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libGCDWebServers.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -204,14 +236,16 @@ F63FF32C1C16432E00B3B8E0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; F63FF32E1C16433900B3B8E0 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; }; F63FF3301C16434400B3B8E0 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + F6874A351CAC792D00EEEE36 /* node_types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_types.hpp; sourceTree = ""; }; + F6874A3E1CACA5A900EEEE36 /* js_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_types.hpp; sourceTree = ""; }; F68A278A1BC2722A0063D40A /* RJSModuleLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RJSModuleLoader.h; path = ios/RJSModuleLoader.h; sourceTree = ""; }; F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RJSModuleLoader.m; path = ios/RJSModuleLoader.m; sourceTree = ""; }; F6BB7DEF1BF681BC00D0A69E /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = ""; }; F6BB7DF01BF681BC00D0A69E /* base64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = ""; }; F6BCCFDF1C83809A00FE31AE /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = ../../lib; sourceTree = SOURCE_ROOT; }; F6C3FBBC1BF680EC00E6FFD4 /* json.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = ""; }; - F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = js_collection.cpp; sourceTree = ""; }; - F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = ""; }; + F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_collection.cpp; sourceTree = ""; }; + F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_collection.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -233,6 +267,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F62BF8F81CAC71780022BCDC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F63FF2ED1C16405C00B3B8E0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -248,13 +290,11 @@ children = ( F6BCCFDF1C83809A00FE31AE /* lib */, F62A35131C18E6E2004A917D /* iOS */, + F6874A441CAD2ACD00EEEE36 /* JSC */, + F62BF9001CAC72C40022BCDC /* Node */, F62A35141C18E783004A917D /* Object Store */, - 029048011C0428DF00ABDED4 /* js_init.cpp */, - 029048021C0428DF00ABDED4 /* js_init.h */, - F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */, - F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */, 029048041C0428DF00ABDED4 /* js_list.hpp */, - 029048051C0428DF00ABDED4 /* js_object.cpp */, + F620F0591CB7B4C80082977B /* js_object_accessor.hpp */, 029048061C0428DF00ABDED4 /* js_object.hpp */, 029048071C0428DF00ABDED4 /* js_realm.cpp */, 029048081C0428DF00ABDED4 /* js_realm.hpp */, @@ -262,14 +302,9 @@ 0290480A1C0428DF00ABDED4 /* js_results.hpp */, 0290480B1C0428DF00ABDED4 /* js_schema.cpp */, 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, - 0290480D1C0428DF00ABDED4 /* js_util.cpp */, - 0290480E1C0428DF00ABDED4 /* js_util.hpp */, - 02AFE5B11CAB133500953DA3 /* js_compat.hpp */, - 025678951CAB392000FB8501 /* types.hpp */, - 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, - 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, - 025678961CAB478800FB8501 /* jsc_realm.cpp */, - 025678971CAB478800FB8501 /* jsc_realm.hpp */, + F620F0521CAF0B600082977B /* js_class.hpp */, + F6874A3E1CACA5A900EEEE36 /* js_types.hpp */, + F6267BC91CADC30000AC36B1 /* js_util.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, 0290480F1C0428DF00ABDED4 /* rpc.cpp */, 029048101C0428DF00ABDED4 /* rpc.hpp */, @@ -296,6 +331,7 @@ 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */, F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */, F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */, + F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */, ); name = Products; sourceTree = ""; @@ -318,6 +354,7 @@ 02B58CCF1AE99D8C009B348C /* Frameworks */ = { isa = PBXGroup; children = ( + F620F0741CB9F60C0082977B /* CoreFoundation.framework */, 02A3C7A41BC4341500B1A7BE /* libc++.tbd */, F63FF3301C16434400B3B8E0 /* libz.tbd */, F63FF32E1C16433900B3B8E0 /* libxml2.tbd */, @@ -383,6 +420,21 @@ path = "object-store"; sourceTree = ""; }; + F62BF9001CAC72C40022BCDC /* Node */ = { + isa = PBXGroup; + children = ( + F6267BCA1CADC49200AC36B1 /* node_dummy.c */, + F60102CF1CBB814A00EC01BA /* node_init.hpp */, + F620F0571CB766DA0082977B /* node_init.cpp */, + F620F0551CB655A50082977B /* node_class.hpp */, + F6874A351CAC792D00EEEE36 /* node_types.hpp */, + F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */, + F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */, + ); + name = Node; + path = node; + sourceTree = ""; + }; F63FF2FB1C1642BB00B3B8E0 /* GCDWebServer */ = { isa = PBXGroup; children = ( @@ -443,6 +495,28 @@ path = Responses; sourceTree = ""; }; + F6874A441CAD2ACD00EEEE36 /* JSC */ = { + isa = PBXGroup; + children = ( + 029048021C0428DF00ABDED4 /* jsc_init.h */, + F60102D11CBB865A00EC01BA /* jsc_init.hpp */, + 029048011C0428DF00ABDED4 /* jsc_init.cpp */, + F620F0531CAF2EF70082977B /* jsc_class.hpp */, + 025678951CAB392000FB8501 /* jsc_types.hpp */, + 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */, + F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */, + F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */, + 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, + 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, + 025678971CAB478800FB8501 /* jsc_realm.hpp */, + 025678961CAB478800FB8501 /* jsc_realm.cpp */, + F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */, + F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */, + ); + name = JSC; + path = jsc; + sourceTree = ""; + }; F6C3FBB41BF680D000E6FFD4 /* Vendor */ = { isa = PBXGroup; children = ( @@ -463,20 +537,28 @@ buildActionMask = 2147483647; files = ( 0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */, - 029048121C0428DF00ABDED4 /* js_init.h in Headers */, + 029048121C0428DF00ABDED4 /* jsc_init.h in Headers */, 029048201C0428DF00ABDED4 /* rpc.hpp in Headers */, - 0290481E1C0428DF00ABDED4 /* js_util.hpp in Headers */, + 0290481E1C0428DF00ABDED4 /* jsc_util.hpp in Headers */, 0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */, 029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */, 029048141C0428DF00ABDED4 /* js_list.hpp in Headers */, F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */, - F6CB31011C8EDDBB0070EF3F /* js_collection.hpp in Headers */, + F6CB31011C8EDDBB0070EF3F /* jsc_collection.hpp in Headers */, 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */, 029048161C0428DF00ABDED4 /* js_object.hpp in Headers */, 029048371C042A3C00ABDED4 /* platform.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; + F62BF8F91CAC71780022BCDC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -518,6 +600,23 @@ productReference = 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + F62BF8FA1CAC71780022BCDC /* RealmNode */ = { + isa = PBXNativeTarget; + buildConfigurationList = F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */; + buildPhases = ( + F62BF8F71CAC71780022BCDC /* Sources */, + F62BF8F81CAC71780022BCDC /* Frameworks */, + F62BF8F91CAC71780022BCDC /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RealmNode; + productName = RealmNode; + productReference = F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; F63FF2B01C1241E500B3B8E0 /* RealmJS static */ = { isa = PBXNativeTarget; buildConfigurationList = F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS static" */; @@ -566,6 +665,9 @@ 02B58CBB1AE99CEC009B348C = { CreatedOnToolsVersion = 6.3.1; }; + F62BF8FA1CAC71780022BCDC = { + CreatedOnToolsVersion = 7.3; + }; F63FF2B01C1241E500B3B8E0 = { CreatedOnToolsVersion = 7.1.1; }; @@ -588,6 +690,7 @@ targets = ( F63FF2EF1C16405C00B3B8E0 /* GCDWebServers */, F63FF2B01C1241E500B3B8E0 /* RealmJS static */, + F62BF8FA1CAC71780022BCDC /* RealmNode */, 02B58CB01AE99CEC009B348C /* RealmJS */, 02B58CBB1AE99CEC009B348C /* RealmJSTests */, ); @@ -655,20 +758,45 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F62BF8F71CAC71780022BCDC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */, + F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */, + F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */, + F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */, + F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */, + F60102D51CBB96AE00EC01BA /* list.cpp in Sources */, + F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */, + F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */, + F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */, + F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */, + F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */, + F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */, + F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */, + F60102D81CBB96BD00EC01BA /* results.cpp in Sources */, + F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */, + F620F0581CB766DA0082977B /* node_init.cpp in Sources */, + F60102D91CBB96C100EC01BA /* schema.cpp in Sources */, + F60102E91CBCAEC500EC01BA /* platform.mm in Sources */, + F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F63FF2AD1C1241E500B3B8E0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F6CB31001C8EDDAB0070EF3F /* js_collection.cpp in Sources */, + F6CB31001C8EDDAB0070EF3F /* jsc_collection.cpp in Sources */, 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */, 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */, F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */, 02F59EC31C88F17D007F774C /* results.cpp in Sources */, 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */, F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */, - F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */, + F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */, 02F59ECA1C88F190007F774C /* parser.cpp in Sources */, - F63FF2C81C12469E00B3B8E0 /* js_object.cpp in Sources */, 02F59EE11C88F2BB007F774C /* async_query.cpp in Sources */, 02F59EC01C88F17D007F774C /* list.cpp in Sources */, 02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */, @@ -677,8 +805,8 @@ F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */, 02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */, F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */, - F63FF2CC1C12469E00B3B8E0 /* js_util.cpp in Sources */, 02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */, + F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */, 02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */, 02F59EC41C88F17D007F774C /* schema.cpp in Sources */, F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */, @@ -771,6 +899,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../object-store/external/pegtl", + "$(SRCROOT)/../object-store/src", + "$(SRCROOT)/../../vendor", + ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; LIBRARY_SEARCH_PATHS = ../../core; MTL_ENABLE_DEBUG_INFO = YES; @@ -823,6 +956,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../object-store/external/pegtl", + "$(SRCROOT)/../object-store/src", + "$(SRCROOT)/../../vendor", + ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; LIBRARY_SEARCH_PATHS = ../../core; MTL_ENABLE_DEBUG_INFO = NO; @@ -958,6 +1096,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../object-store/external/pegtl", + "$(SRCROOT)/../object-store/src", + "$(SRCROOT)/../../vendor", + ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; LIBRARY_SEARCH_PATHS = ../../core; MTL_ENABLE_DEBUG_INFO = YES; @@ -1016,6 +1159,95 @@ }; name = GCov_Build; }; + F62BF8FC1CAC71780022BCDC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../node_modules/nan", + /usr/local/include/node, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/lib, + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(inherited)", + "-ftemplate-backtrace-limit=0", + ); + OTHER_LDFLAGS = ( + "-lrealm", + "-lv8", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + F62BF8FD1CAC71780022BCDC /* GCov_Build */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../node_modules/nan", + /usr/local/include/node, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/lib, + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(inherited)", + "-ftemplate-backtrace-limit=0", + ); + OTHER_LDFLAGS = ( + "-lrealm", + "-lv8", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = GCov_Build; + }; + F62BF8FE1CAC71780022BCDC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../node_modules/nan", + /usr/local/include/node, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/lib, + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(inherited)", + "-ftemplate-backtrace-limit=0", + ); + OTHER_LDFLAGS = ( + "-lrealm", + "-lv8", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; F63FF2B71C1241E500B3B8E0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1024,12 +1256,6 @@ "DEBUG=1", "$(inherited)", ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../object-store/external/pegtl", - "$(SRCROOT)/../object-store/src", - "$(SRCROOT)/../../vendor", - ); PRODUCT_NAME = RealmJS; SKIP_INSTALL = YES; }; @@ -1038,12 +1264,6 @@ F63FF2B81C1241E500B3B8E0 /* GCov_Build */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../object-store/external/pegtl", - "$(SRCROOT)/../object-store/src", - "$(SRCROOT)/../../vendor", - ); PRODUCT_NAME = RealmJS; SKIP_INSTALL = YES; }; @@ -1052,12 +1272,6 @@ F63FF2B91C1241E500B3B8E0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../object-store/external/pegtl", - "$(SRCROOT)/../object-store/src", - "$(SRCROOT)/../../vendor", - ); PRODUCT_NAME = RealmJS; SKIP_INSTALL = YES; }; @@ -1071,6 +1285,7 @@ "DEBUG=1", "$(inherited)", ); + HEADER_SEARCH_PATHS = ""; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1080,6 +1295,7 @@ F63FF2F71C16405D00B3B8E0 /* GCov_Build */ = { isa = XCBuildConfiguration; buildSettings = { + HEADER_SEARCH_PATHS = ""; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1089,6 +1305,7 @@ F63FF2F81C16405D00B3B8E0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + HEADER_SEARCH_PATHS = ""; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1128,6 +1345,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F62BF8FC1CAC71780022BCDC /* Debug */, + F62BF8FD1CAC71780022BCDC /* GCov_Build */, + F62BF8FE1CAC71780022BCDC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS static" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/js_class.hpp b/src/js_class.hpp new file mode 100644 index 00000000..6168eedb --- /dev/null +++ b/src/js_class.hpp @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +#include "js_types.hpp" + +namespace realm { +namespace js { + +template +using ConstructorType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[]); + +template +using MethodType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[], ReturnValue &); + +template +using PropertyGetterType = void(typename T::Context, typename T::Object, ReturnValue &); + +template +using PropertySetterType = void(typename T::Context, typename T::Object, typename T::Value); + +template +using IndexPropertyGetterType = void(typename T::Context, typename T::Object, uint32_t, ReturnValue &); + +template +using IndexPropertySetterType = bool(typename T::Context, typename T::Object, uint32_t, typename T::Value); + +template +using StringPropertyGetterType = void(typename T::Context, typename T::Object, const String &, ReturnValue &); + +template +using StringPropertySetterType = bool(typename T::Context, typename T::Object, const String &, typename T::Value); + +template +using StringPropertyEnumeratorType = std::vector>(typename T::Context, typename T::Object); + +template +struct PropertyType { + typename T::PropertyGetterCallback getter; + typename T::PropertySetterCallback setter; +}; + +template +struct IndexPropertyType { + typename T::IndexPropertyGetterCallback getter; + typename T::IndexPropertySetterCallback setter; +}; + +template +struct StringPropertyType { + typename T::StringPropertyGetterCallback getter; + typename T::StringPropertySetterCallback setter; + typename T::StringPropertyEnumeratorCallback enumerator; +}; + +template +using MethodMap = std::map; + +template +using PropertyMap = std::map>; + +template +struct BaseObjectClass { + std::string name; + BaseObjectClass* superclass; + + ConstructorType* constructor; + MethodMap static_methods = MethodMap(); + PropertyMap static_properties = PropertyMap(); + MethodMap methods = MethodMap(); + PropertyMap properties = PropertyMap(); + IndexPropertyType index_accessor = {}; + StringPropertyType string_accessor = {}; +}; + +template +struct ObjectClass; + +} // js +} // realm diff --git a/src/js_list.hpp b/src/js_list.hpp index 9bf07f48..87f2113a 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -18,20 +18,19 @@ #pragma once -#include "js_collection.hpp" +#include + +#include "js_class.hpp" #include "js_object.hpp" -#include "js_results.hpp" +// TODO: #include "js_results.hpp" +#include "js_types.hpp" #include "js_util.hpp" #include "shared_realm.hpp" #include "list.hpp" -#include "object_accessor.hpp" #include "parser.hpp" #include "query_builder.hpp" -#include - -using RJSAccessor = realm::NativeAccessor; namespace realm { namespace js { @@ -40,148 +39,203 @@ struct List { using ContextType = typename T::Context; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using ReturnType = typename T::Return; - - static void GetLength(ContextType ctx, ObjectType thisObject, ReturnType &ret); - static void GetIndex(ContextType ctx, ObjectType thisObject, size_t index, ReturnType &ret); - static void SetIndex(ContextType ctx, ObjectType thisObject, size_t index, ValueType value); + using Object = Object; + using Value = Value; + using ReturnValue = ReturnValue; - static void Push(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Pop(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Unshift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Shift(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Splice(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void StaticResults(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Filtered(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); - static void Sorted(ContextType ctx, ObjectType thisObject, size_t argCount, const ValueType args[], ReturnType &ret); + static ObjectType create(ContextType, realm::List &); + + static void GetLength(ContextType, ObjectType, ReturnValue &); + static void GetIndex(ContextType, ObjectType, uint32_t, ReturnValue &); + static bool SetIndex(ContextType, ObjectType, uint32_t, ValueType); + + static void Push(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Pop(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Unshift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Shift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Splice(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void StaticResults(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); }; - + template -void List::GetLength(ContextType ctx, ObjectType object, ReturnType &ret) { - realm::List *list = RJSGetInternal(object); - RJSSetReturnNumber(ctx, ret, list->size()); -} - +struct ObjectClass : BaseObjectClass { + using List = List; + + std::string const name = "List"; + + MethodMap const methods = { + {"push", wrap}, + {"pop", wrap}, + {"unshift", wrap}, + {"shift", wrap}, + {"splice", wrap}, + {"snapshot", wrap}, + {"filtered", wrap}, + {"sorted", wrap}, + }; + + PropertyMap const properties = { + {"length", {wrap}}, + }; + + IndexPropertyType const index_accessor = {wrap, wrap}; +}; + template -void List::GetIndex(ContextType ctx, ObjectType object, size_t index, ReturnType &ret) { - realm::List *list = RJSGetInternal(object); - ret = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); +typename T::Object List::create(ContextType ctx, realm::List &list) { + return create_object(ctx, new realm::List(list)); } template -void List::SetIndex(ContextType ctx, ObjectType object, size_t index, ValueType value) { - realm::List *list = RJSGetInternal(object); +void List::GetLength(ContextType ctx, ObjectType object, ReturnValue &return_value) { + auto list = get_internal(object); + return_value.set((uint32_t)list->size()); +} + +template +void List::GetIndex(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { + auto list = get_internal(object); + auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); + + return_value.set(RealmObject::create(ctx, realm_object)); +} + +template +bool List::SetIndex(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { + auto list = get_internal(object); list->set(ctx, value, index); + return true; } template -void List::Push(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { +void List::Push(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count_at_least(argc, 1); + + auto list = get_internal(this_object); + for (size_t i = 0; i < argc; i++) { list->add(ctx, arguments[i]); } - RJSSetReturnNumber(ctx, returnObject, list->size()); + + return_value.set((uint32_t)list->size()); } template -void List::Pop(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - +void List::Pop(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + auto list = get_internal(this_object); size_t size = list->size(); if (size == 0) { list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); + return_value.set_undefined(); } else { size_t index = size - 1; - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); + + return_value.set(RealmObject::create(ctx, realm_object)); list->remove(index); } } - template -void List::Unshift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - for (size_t i = 0; i < argumentCount; i++) { +void List::Unshift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count_at_least(argc, 1); + + auto list = get_internal(this_object); + for (size_t i = 0; i < argc; i++) { list->insert(ctx, arguments[i], i); } - RJSSetReturnNumber(ctx, returnObject, list->size()); + + return_value.set((uint32_t)list->size()); } template -void List::Shift(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); +void List::Shift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + auto list = get_internal(this_object); if (list->size() == 0) { list->verify_in_transaction(); - RJSSetReturnUndefined(ctx, returnObject); + return_value.set_undefined(); } else { - returnObject = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0))); + auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(0)); + + return_value.set(RealmObject::create(ctx, realm_object)); list->remove(0); } } template -void List::Splice(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); +void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count_at_least(argc, 1); + + auto list = get_internal(this_object); size_t size = list->size(); - - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - long index = std::min(RJSValidatedValueToNumber(ctx, arguments[0]), size); + long index = std::min(Value::validated_to_number(ctx, arguments[0]), size); if (index < 0) { index = std::max(size + index, 0); } long remove; - if (argumentCount < 2) { + if (argc < 2) { remove = size - index; } else { - remove = std::max(RJSValidatedValueToNumber(ctx, arguments[1]), 0); + remove = std::max(Value::validated_to_number(ctx, arguments[1]), 0); remove = std::min(remove, size - index); } - std::vector removedObjects(remove); + std::vector removed_objects; + removed_objects.reserve(remove); + for (size_t i = 0; i < remove; i++) { - removedObjects[i] = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index))); + auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); + + removed_objects.push_back(RealmObject::create(ctx, realm_object)); list->remove(index); } - for (size_t i = 2; i < argumentCount; i++) { + for (size_t i = 2; i < argc; i++) { list->insert(ctx, arguments[i], index + i - 2); } - RJSSetReturnArray(ctx, remove, removedObjects.data(), returnObject); -} - -template -void List::StaticResults(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); - returnObject = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); + return_value.set(Object::create_array(ctx, removed_objects)); } template -void List::Filtered(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); +void List::StaticResults(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + auto list = get_internal(this_object); + + // TODO: Results + // return_value = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); } template -void List::Sorted(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - realm::List *list = RJSGetInternal(thisObject); - RJSValidateArgumentRange(argumentCount, 1, 2); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments); +void List::Filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count_at_least(argc, 1); + + auto list = get_internal(this_object); + auto sharedRealm = *get_internal(this_object); + + // TODO: Results + // return_value = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argc, arguments); +} + +template +void List::Sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1, 2); + + auto list = get_internal(this_object); + auto sharedRealm = *get_internal(this_object); + + // TODO: Results + // return_value = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argc, arguments); } -} -} +} // js +} // realm diff --git a/src/js_object.cpp b/src/js_object.cpp deleted file mode 100644 index c6c7b671..00000000 --- a/src/js_object.cpp +++ /dev/null @@ -1,295 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_util.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "js_schema.hpp" -#include "jsc_list.hpp" -#include "js_realm.hpp" - -#include "object_store.hpp" -#include "object_accessor.hpp" - -using RJSAccessor = realm::NativeAccessor; -using namespace realm; - -JSValueRef ObjectGetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef* exception) { - try { - Object *obj = RJSGetInternal(jsObject); - return obj->get_property_value(ctx, RJSStringForJSString(jsPropertyName)); - } catch (InvalidPropertyException &ex) { - // getters for nonexistent properties in JS should always return undefined - } catch (std::exception &ex) { - if (exception) { - *exception = RJSMakeError(ctx, ex); - } - } - return NULL; -} - -bool ObjectSetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef value, JSValueRef* exception) { - try { - Object *obj = RJSGetInternal(jsObject); - obj->set_property_value(ctx, RJSStringForJSString(jsPropertyName), value, true); - } catch (std::exception &ex) { - if (exception) { - *exception = RJSMakeError(ctx, ex); - } - return false; - } - return true; -} - -void ObjectPropertyNames(JSContextRef ctx, JSObjectRef jsObject, JSPropertyNameAccumulatorRef propertyNames) { - Object *obj = RJSGetInternal(jsObject); - - for (auto &prop : obj->get_object_schema().properties) { - JSStringRef propertyName = RJSStringForString(prop.name); - JSPropertyNameAccumulatorAddName(propertyNames, propertyName); - JSStringRelease(propertyName); - } -} - -JSClassRef RJSObjectClass() { - static JSClassRef s_objectClass = RJSCreateWrapperClass("RealmObject", ObjectGetProperty, ObjectSetProperty, NULL, ObjectPropertyNames); - return s_objectClass; -} - -namespace realm { - namespace js { - JSClassRef object_class() { return RJSObjectClass(); }; - } -} - -JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) { - static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype"); - - auto delegate = realm::js::get_delegate(object.realm().get()); - JSObjectRef constructor = delegate->m_constructors[object.get_object_schema().name]; - JSObjectRef prototype = constructor ? RJSValidatedObjectProperty(ctx, constructor, prototypeString) : NULL; - JSObjectRef jsObject = realm::js::WrapObject(ctx, RJSObjectClass(), new Object(object), prototype); - - if (constructor) { - JSValueRef exception = NULL; - JSValueRef result = JSObjectCallAsFunction(ctx, constructor, jsObject, 0, NULL, &exception); - - if (exception) { - throw RJSException(ctx, exception); - } - else if (result != jsObject && !JSValueIsNull(ctx, result) && !JSValueIsUndefined(ctx, result)) { - throw std::runtime_error("Realm object constructor must not return another value"); - } - } - - return jsObject; -} - -namespace realm { - -template<> bool RJSAccessor::dict_has_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) { - JSObjectRef object = RJSValidatedValueToObject(ctx, dict); - JSStringRef propStr = RJSStringForString(prop_name); - bool ret = JSObjectHasProperty(ctx, object, propStr); - - JSStringRelease(propStr); - return ret; -} - -template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) { - JSObjectRef object = RJSValidatedValueToObject(ctx, dict); - JSStringRef propStr = RJSStringForString(prop_name); - JSValueRef ex = NULL; - JSValueRef ret = JSObjectGetProperty(ctx, object, propStr, &ex); - if (ex) { - throw RJSException(ctx, ex); - } - JSStringRelease(propStr); - return ret; -} - -template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; - return defaults.find(prop_name) != defaults.end(); -} - -template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { - auto defaults = realm::js::get_delegate(realm)->m_defaults[object_schema.name]; - return defaults[prop_name]; -} - -template<> bool RJSAccessor::is_null(JSContextRef ctx, JSValueRef &val) { - return JSValueIsNull(ctx, val) || JSValueIsUndefined(ctx, val); -} -template<> JSValueRef RJSAccessor::null_value(JSContextRef ctx) { - return JSValueMakeNull(ctx); -} - -template<> bool RJSAccessor::to_bool(JSContextRef ctx, JSValueRef &val) { - if (!JSValueIsBoolean(ctx, val)) { - throw std::runtime_error("Property expected to be of type boolean"); - } - return JSValueToBoolean(ctx, val); -} -template<> JSValueRef RJSAccessor::from_bool(JSContextRef ctx, bool b) { - return JSValueMakeBoolean(ctx, b); -} - -template<> long long RJSAccessor::to_long(JSContextRef ctx, JSValueRef &val) { - return RJSValidatedValueToNumber(ctx, val); -} -template<> JSValueRef RJSAccessor::from_long(JSContextRef ctx, long long l) { - return JSValueMakeNumber(ctx, l); -} - -template<> float RJSAccessor::to_float(JSContextRef ctx, JSValueRef &val) { - return RJSValidatedValueToNumber(ctx, val); -} -template<> JSValueRef RJSAccessor::from_float(JSContextRef ctx, float f) { - return JSValueMakeNumber(ctx, f); -} - -template<> double RJSAccessor::to_double(JSContextRef ctx, JSValueRef &val) { - return RJSValidatedValueToNumber(ctx, val); -} -template<> JSValueRef RJSAccessor::from_double(JSContextRef ctx, double d) { - return JSValueMakeNumber(ctx, d); -} - -template<> std::string RJSAccessor::to_string(JSContextRef ctx, JSValueRef &val) { - return RJSValidatedStringForValue(ctx, val); -} -template<> JSValueRef RJSAccessor::from_string(JSContextRef ctx, StringData s) { - return RJSValueForString(ctx, s); -} - -template<> std::string RJSAccessor::to_binary(JSContextRef ctx, JSValueRef &val) { - static JSStringRef arrayBufferString = JSStringCreateWithUTF8CString("ArrayBuffer"); - static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer"); - static JSStringRef byteLengthString = JSStringCreateWithUTF8CString("byteLength"); - static JSStringRef byteOffsetString = JSStringCreateWithUTF8CString("byteOffset"); - static JSStringRef isViewString = JSStringCreateWithUTF8CString("isView"); - static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array"); - - JSObjectRef arrayBufferConstructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), arrayBufferString); - JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString); - JSValueRef uint8ArrayArguments[3]; - size_t uint8ArrayArgumentsCount = 0; - - // Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView). - if (JSValueIsInstanceOfConstructor(ctx, val, arrayBufferConstructor, NULL)) { - uint8ArrayArguments[0] = val; - uint8ArrayArgumentsCount = 1; - } - else if (JSObjectRef object = JSValueToObject(ctx, val, NULL)) { - // Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val). - JSObjectRef isViewMethod = RJSValidatedObjectProperty(ctx, arrayBufferConstructor, isViewString); - JSValueRef isView = JSObjectCallAsFunction(ctx, isViewMethod, arrayBufferConstructor, 1, &val, NULL); - - if (isView && JSValueToBoolean(ctx, isView)) { - uint8ArrayArguments[0] = RJSValidatedObjectProperty(ctx, object, bufferString); - uint8ArrayArguments[1] = RJSValidatedPropertyValue(ctx, object, byteOffsetString); - uint8ArrayArguments[2] = RJSValidatedPropertyValue(ctx, object, byteLengthString); - uint8ArrayArgumentsCount = 3; - } - } - - if (!uint8ArrayArgumentsCount) { - throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary"); - } - - JSValueRef exception = NULL; - JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, uint8ArrayArgumentsCount, uint8ArrayArguments, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - - size_t byteCount = RJSValidatedListLength(ctx, uint8Array); - std::string bytes(byteCount, 0); - - for (size_t i = 0; i < byteCount; i++) { - JSValueRef byteValue = JSObjectGetPropertyAtIndex(ctx, uint8Array, (unsigned)i, NULL); - bytes[i] = JSValueToNumber(ctx, byteValue, NULL); - } - - return bytes; -} -template<> JSValueRef RJSAccessor::from_binary(JSContextRef ctx, BinaryData data) { - static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer"); - static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array"); - - size_t byteCount = data.size(); - JSValueRef byteCountValue = JSValueMakeNumber(ctx, byteCount); - JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString); - JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, 1, &byteCountValue, NULL); - - for (size_t i = 0; i < byteCount; i++) { - JSValueRef num = JSValueMakeNumber(ctx, data[i]); - JSObjectSetPropertyAtIndex(ctx, uint8Array, (unsigned)i, num, NULL); - } - - return RJSValidatedObjectProperty(ctx, uint8Array, bufferString); -} - -template<> DateTime RJSAccessor::to_datetime(JSContextRef ctx, JSValueRef &val) { - JSObjectRef object = RJSValidatedValueToDate(ctx, val); - double utc = RJSValidatedValueToNumber(ctx, object); - - return DateTime(utc); -} -template<> JSValueRef RJSAccessor::from_datetime(JSContextRef ctx, DateTime dt) { - JSValueRef time = JSValueMakeNumber(ctx, dt.get_datetime()); - return JSObjectMakeDate(ctx, 1, &time, NULL); -} - -template<> size_t RJSAccessor::to_existing_object_index(JSContextRef ctx, JSValueRef &val) { - JSObjectRef object = RJSValidatedValueToObject(ctx, val); - if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) { - return RJSGetInternal(object)->row().get_index(); - } - throw std::runtime_error("object is not a Realm Object"); -} -template<> size_t RJSAccessor::to_object_index(JSContextRef ctx, SharedRealm realm, JSValueRef &val, const std::string &type, bool try_update) { - JSObjectRef object = RJSValidatedValueToObject(ctx, val); - if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) { - return RJSGetInternal(object)->row().get_index(); - } - - auto object_schema = realm->config().schema->find(type); - if (RJSIsValueArray(ctx, object)) { - object = RJSDictForPropertyArray(ctx, *object_schema, object); - } - - Object child = Object::create(ctx, realm, *object_schema, (JSValueRef)object, try_update); - return child.row().get_index(); -} -template<> JSValueRef RJSAccessor::from_object(JSContextRef ctx, Object object) { - return RJSObjectCreate(ctx, object); -} - -template<> size_t RJSAccessor::list_size(JSContextRef ctx, JSValueRef &val) { - return RJSValidatedListLength(ctx, RJSValidatedValueToObject(ctx, val)); -} -template<> JSValueRef RJSAccessor::list_value_at_index(JSContextRef ctx, JSValueRef &val, size_t index) { - return RJSValidatedObjectAtIndex(ctx, RJSValidatedValueToObject(ctx, val), (unsigned int)index); -} -template<> JSValueRef RJSAccessor::from_list(JSContextRef ctx, List list) { - return RJSListCreate(ctx, list); -} - -} \ No newline at end of file diff --git a/src/js_object.hpp b/src/js_object.hpp index 8a9a472c..a6782b2d 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -18,11 +18,103 @@ #pragma once +#include "js_class.hpp" +#include "js_types.hpp" #include "js_util.hpp" +#include "object_accessor.hpp" +#include "object_store.hpp" + namespace realm { - class Object; +namespace js { + +template +struct RealmObject { + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using String = String; + using Value = Value; + using Object = Object; + using Function = Function; + using ReturnValue = ReturnValue; + + static ObjectType create(ContextType, realm::Object &); + + static void GetProperty(ContextType, ObjectType, const String &, ReturnValue &); + static bool SetProperty(ContextType, ObjectType, const String &, ValueType, ReturnValue &); + static std::vector GetPropertyNames(ContextType, ObjectType); +}; + +template +struct ObjectClass : BaseObjectClass { + using RealmObject = RealmObject; + + const std::string name = "RealmObject"; + + const StringPropertyType string_accessor = { + wrap, + wrap, + wrap, + }; +}; + +template +typename T::Object RealmObject::create(ContextType ctx, realm::Object &realm_object) { + static String s_prototype = "prototype"; + + auto delegate = get_delegate(realm_object.realm().get()); + auto name = realm_object.get_object_schema().name; + auto object = create_object(ctx, new realm::Object(realm_object)); + + if (!delegate->m_constructors.count(name)) { + return object; + } + + FunctionType constructor = delegate->m_constructors.at(name); + ObjectType prototype = Object::validated_get_object(ctx, constructor, s_prototype); + Object::set_prototype(ctx, object, prototype); + + ValueType result = Function::call(ctx, constructor, object, 0, NULL); + if (result != object && !Value::is_null(ctx, result) && !Value::is_undefined(ctx, result)) { + throw std::runtime_error("Realm object constructor must not return another value"); + } + + return object; } -JSClassRef RJSObjectClass(); -JSObjectRef RJSObjectCreate(JSContextRef ctx, realm::Object object); +template +void RealmObject::GetProperty(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { + try { + auto realm_object = get_internal(object); + auto result = realm_object->template get_property_value(ctx, property); + return_value.set(result); + } catch (InvalidPropertyException &ex) { + // getters for nonexistent properties in JS should always return undefined + } +} + +template +bool RealmObject::SetProperty(ContextType ctx, ObjectType object, const String &property, ValueType value, ReturnValue &return_value) { + auto realm_object = get_internal(object); + realm_object->set_property_value(ctx, property, value, true); + return true; +} + +template +std::vector> RealmObject::GetPropertyNames(ContextType ctx, ObjectType object) { + auto realm_object = get_internal(object); + auto &properties = realm_object->get_object_schema().properties; + std::vector names(properties.size()); + size_t index = 0; + + for (auto &prop : properties) { + names[index++] = prop.name; + } + + return names; +} + +} // js +} // realm diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp new file mode 100644 index 00000000..c3d27a5d --- /dev/null +++ b/src/js_object_accessor.hpp @@ -0,0 +1,145 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "js_list.hpp" +#include "js_object.hpp" +#include "js_schema.hpp" + +namespace realm { +namespace js { + +template +class NativeAccessor { + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using Object = Object; + using Value = Value; + + public: + static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { + ObjectType object = Value::validated_to_object(ctx, dict); + return Object::has_property(ctx, object, prop_name); + } + static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { + ObjectType object = Value::validated_to_object(ctx, dict); + return Object::get_property(ctx, object, prop_name); + } + + static bool has_default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; + return defaults.count(prop_name) != 0; + } + static ValueType default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; + return defaults.at(prop_name); + } + + // These must be implemented for each JS engine. + static std::string to_binary(ContextType, ValueType &); + static ValueType from_binary(ContextType, BinaryData); + + static bool to_bool(ContextType ctx, ValueType &value) { + return Value::validated_to_boolean(ctx, value, "Property"); + } + static ValueType from_bool(ContextType ctx, bool boolean) { + return Value::from_boolean(ctx, boolean); + } + static long long to_long(ContextType ctx, ValueType &value) { + return Value::validated_to_number(ctx, value, "Property"); + } + static ValueType from_long(ContextType ctx, long long number) { + return Value::from_number(ctx, number); + } + static float to_float(ContextType ctx, ValueType &value) { + return Value::validated_to_number(ctx, value, "Property"); + } + static ValueType from_float(ContextType ctx, float number) { + return Value::from_number(ctx, number); + } + static double to_double(ContextType ctx, ValueType &value) { + return Value::validated_to_number(ctx, value, "Property"); + } + static ValueType from_double(ContextType ctx, double number) { + return Value::from_number(ctx, number); + } + static std::string to_string(ContextType ctx, ValueType &value) { + return Value::validated_to_string(ctx, value, "Property"); + } + static ValueType from_string(ContextType ctx, StringData string) { + return Value::from_string(ctx, string); + } + static DateTime to_datetime(ContextType ctx, ValueType &value) { + ObjectType date = Value::validated_to_date(ctx, value, "Property"); + return DateTime(Value::to_number(ctx, date)); + } + static ValueType from_datetime(ContextType ctx, DateTime dt) { + return Object::create_date(ctx, dt.get_datetime()); + } + + static bool is_null(ContextType ctx, ValueType &value) { + return Value::is_null(ctx, value) || Value::is_undefined(ctx, value); + } + static ValueType null_value(ContextType ctx) { + return Value::from_null(ctx); + } + + static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &value, const std::string &type, bool try_update) { + ObjectType object = Value::validated_to_object(ctx, value); + if (Object::template is_instance(ctx, object)) { + return get_internal(object)->row().get_index(); + } + + auto object_schema = realm->config().schema->find(type); + if (Value::is_array(ctx, object)) { + object = Schema::dict_for_property_array(ctx, *object_schema, object); + } + + auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); + return child.row().get_index(); + } + static size_t to_existing_object_index(ContextType ctx, ValueType &value) { + ObjectType object = Value::validated_to_object(ctx, value); + if (Object::template is_instance(ctx, object)) { + return get_internal(object)->row().get_index(); + } + throw std::runtime_error("object is not a Realm Object"); + } + static ValueType from_object(ContextType ctx, realm::Object realm_object) { + return RealmObject::create(ctx, realm_object); + } + + static size_t list_size(ContextType ctx, ValueType &value) { + return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value)); + } + static ValueType list_value_at_index(ContextType ctx, ValueType &value, size_t index) { + return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index); + } + static ValueType from_list(ContextType ctx, realm::List list) { + return List::create(ctx, list); + } + + static Mixed to_mixed(ContextType ctx, ValueType &val) { + throw std::runtime_error("'Any' type is unsupported"); + } +}; + +} // js +} // realm diff --git a/src/js_realm.cpp b/src/js_realm.cpp index 5161da14..e7aedd2a 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////// -#include "js_realm.hpp" #include "platform.hpp" namespace realm { @@ -34,4 +33,5 @@ void set_default_path(std::string path) { s_defaultPath = path; } -}} +} // js +} // realm diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 848ade78..26eb9c81 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -18,7 +18,13 @@ #pragma once +#include +#include + +#include "js_class.hpp" +#include "js_types.hpp" #include "js_util.hpp" +#include "js_object.hpp" #include "js_schema.hpp" #include "shared_realm.hpp" @@ -27,9 +33,6 @@ #include "results.hpp" #include "platform.hpp" -#include -#include - namespace realm { namespace js { @@ -37,10 +40,13 @@ template class RealmDelegate : public BindingContext { public: using GlobalContextType = typename T::GlobalContext; - using ObjectClassType = typename T::ObjectClass; + using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using ObjectDefaults = std::map; + using Value = Value; + + using ObjectDefaultsMap = typename Schema::ObjectDefaultsMap; + using ConstructorMap = typename Schema::ConstructorMap; virtual void did_change(std::vector const& observers, std::vector const& invalidated) { notify("change"); @@ -50,75 +56,47 @@ class RealmDelegate : public BindingContext { } virtual void will_change(std::vector const& observers, std::vector const& invalidated) {} - RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) { - GlobalContextProtect(m_context); - } + RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {} + ~RealmDelegate() { remove_all_notifications(); - - for (auto constructor : m_constructors) { - ValueUnprotect(m_context, constructor.second); - } - for (auto objectDefaults : m_defaults) { - for (auto value : objectDefaults.second) { - ValueUnprotect(m_context, value.second); - } - } - GlobalContextUnprotect(m_context); } - void add_notification(JSObjectRef notification) { - if (!m_notifications.count(notification)) { - ValueProtect(m_context, notification); - m_notifications.insert(notification); - } + void add_notification(FunctionType notification) { + m_notifications.insert(Protected(m_context, notification)); } - void remove_notification(JSObjectRef notification) { - if (m_notifications.count(notification)) { - ValueUnprotect(m_context, notification); - m_notifications.erase(notification); - } + void remove_notification(FunctionType notification) { + m_notifications.erase(Protected(m_context, notification)); } void remove_all_notifications() { - for (auto notification : m_notifications) { - ValueUnprotect(m_context, notification); - } m_notifications.clear(); } - std::map m_defaults; - std::map m_constructors; + ObjectDefaultsMap m_defaults; + ConstructorMap m_constructors; -private: - std::set m_notifications; - GlobalContextType m_context; + private: + Protected m_context; + std::set> m_notifications; std::weak_ptr m_realm; void notify(const char *notification_name) { - JSValueRef arguments[2]; SharedRealm realm = m_realm.lock(); if (!realm) { throw std::runtime_error("Realm no longer exists"); } - ObjectType realm_object = WrapObject(m_context, realm::js::realm_class(), new SharedRealm(realm)); + + ObjectType realm_object = create_object(m_context, new SharedRealm(realm)); + ValueType arguments[2]; arguments[0] = realm_object; - arguments[1] = RJSValueForString(m_context, notification_name); + arguments[1] = Value::from_string(m_context, notification_name); for (auto callback : m_notifications) { - JSValueRef ex = NULL; - ObjectCallAsFunction(m_context, callback, realm_object, 2, arguments, ex); - if (ex) { - throw RJSException(m_context, ex); - } + Function::call(m_context, callback, realm_object, 2, arguments); } } }; -template -RealmDelegate *get_delegate(Realm *realm) { - return dynamic_cast *>(realm->m_binding_context.get()); -} - std::string default_path(); void set_default_path(std::string path); @@ -126,36 +104,40 @@ template class Realm : public BindingContext { public: using ContextType = typename T::Context; + using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using ReturnType = typename T::Return; - using ExceptionType = typename T::Exception; + using String = String; + using Object = Object; + using Value = Value; + using ReturnValue = ReturnValue; + using NativeAccessor = realm::NativeAccessor; // member methods - static void Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - static void Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - + static void Objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Delete(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void DeleteAll(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Write(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void AddListener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void RemoveListener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void RemoveAllListeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Close(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + // properties - static void GetPath(ContextType ctx, ObjectType object, ReturnType &returnObject); - static void GetSchemaVersion(ContextType ctx, ObjectType object, ReturnType &returnObject); + static void GetPath(ContextType, ObjectType, ReturnValue &); + static void GetSchemaVersion(ContextType, ObjectType, ReturnValue &); // constructor methods - static void Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject); - static void SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject); - - // static properties - static void GetDefaultPath(ContextType ctx, ObjectType object, ReturnType &returnObject); - static void SetDefaultPath(ContextType ctx, ObjectType object, ValueType value); + static void Constructor(ContextType, ObjectType, size_t, const ValueType[]); + static void SchemaVersion(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static std::string validated_notification_name(JSContextRef ctx, JSValueRef value) { - std::string name = RJSValidatedStringForValue(ctx, value); + // static properties + static void GetDefaultPath(ContextType, ObjectType, ReturnValue &); + static void SetDefaultPath(ContextType, ObjectType, ValueType value); + + static std::string validated_notification_name(ContextType ctx, const ValueType &value) { + std::string name = Value::validated_to_string(ctx, value, "notification name"); if (name != "change") { throw std::runtime_error("Only the 'change' notification name is supported."); } @@ -163,9 +145,9 @@ public: } // converts constructor object or type name to type name - static std::string validated_object_type_for_value(SharedRealm &realm, JSContextRef ctx, JSValueRef value) { - if (ValueIsObject(ctx, value) && ValueIsConstructor(ctx, value)) { - ObjectType constructor = ValueToObject(ctx, value); + static std::string validated_object_type_for_value(SharedRealm &realm, ContextType ctx, const ValueType &value) { + if (Value::is_constructor(ctx, value)) { + FunctionType constructor = Value::to_constructor(ctx, value); auto delegate = get_delegate(realm.get()); for (auto pair : delegate->m_constructors) { @@ -175,66 +157,91 @@ public: } throw std::runtime_error("Constructor was not registered in the schema for this Realm"); } - return RJSValidatedStringForValue(ctx, value, "objectType"); + return Value::validated_to_string(ctx, value, "objectType"); } - static std::string RJSNormalizePath(std::string path) { + static std::string normalize_path(std::string path) { if (path.size() && path[0] != '/') { return default_realm_file_directory() + "/" + path; } return path; } }; - - + template -void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argumentCount, const ValueType arguments[], ObjectType &returnObject) { - using RJSAccessor = realm::NativeAccessor; - using StringType = typename T::String; - +struct ObjectClass : BaseObjectClass { + using Realm = Realm; + + std::string const name = "Realm"; + + ConstructorType* const constructor = Realm::Constructor; + + MethodMap const methods = { + {"objects", wrap}, + {"create", wrap}, + {"delete", wrap}, + {"deleteAll", wrap}, + {"write", wrap}, + {"addListener", wrap}, + {"removeListener", wrap}, + {"removeAllListeners", wrap}, + {"close", wrap}, + }; + + PropertyMap const properties = { + {"path", {wrap}}, + {"schemaVersion", {wrap}}, + }; +}; + +template +void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[]) { + static const String path_string = "path"; + static const String schema_string = "schema"; + static const String schema_version_string = "schemaVersion"; + static const String encryption_key_string = "encryptionKey"; + realm::Realm::Config config; typename Schema::ObjectDefaultsMap defaults; typename Schema::ConstructorMap constructors; - if (argumentCount == 0) { + + if (argc == 0) { config.path = default_path(); } - else if (argumentCount == 1) { + else if (argc == 1) { ValueType value = arguments[0]; - if (ValueIsString(ctx, value)) { - config.path = RJSValidatedStringForValue(ctx, value, "path"); + if (Value::is_string(ctx, value)) { + config.path = Value::validated_to_string(ctx, value, "path"); } - else if (ValueIsObject(ctx, value)) { - ObjectType object = RJSValidatedValueToObject(ctx, value); - - StringType pathString("path"); - ValueType pathValue = RJSValidatedPropertyValue(ctx, object, pathString); - if (!JSValueIsUndefined(ctx, pathValue)) { - config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); + else if (Value::is_object(ctx, value)) { + ObjectType object = Value::validated_to_object(ctx, value); + + ValueType pathValue = Object::get_property(ctx, object, path_string); + if (!Value::is_undefined(ctx, pathValue)) { + config.path = Value::validated_to_string(ctx, pathValue, "path"); } else { config.path = js::default_path(); } - - StringType schemaString("schema"); - ValueType schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); - if (!ValueIsUndefined(ctx, schemaValue)) { - config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); + + ValueType schemaValue = Object::get_property(ctx, object, schema_string); + if (!Value::is_undefined(ctx, schemaValue)) { + ObjectType schemaObject = Value::validated_to_object(ctx, schemaValue, "schema"); + config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, schemaObject, defaults, constructors))); } - - StringType schemaVersionString("schemaVersion"); - ValueType versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); - if (JSValueIsNumber(ctx, versionValue)) { - config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); + + ValueType versionValue = Object::get_property(ctx, object, schema_version_string); + if (!Value::is_undefined(ctx, versionValue)) { + config.schema_version = Value::validated_to_number(ctx, versionValue, "schemaVersion"); } else { config.schema_version = 0; } - StringType encryptionKeyString("encryptionKey"); - ValueType encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString); - if (!JSValueIsUndefined(ctx, encryptionKeyValue)) { - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end());; + ValueType encryptionKeyValue = Object::get_property(ctx, object, encryption_key_string); + if (!Value::is_undefined(ctx, encryptionKeyValue)) { + std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue); + config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); } } } @@ -242,76 +249,81 @@ void Realm::Constructor(ContextType ctx, ObjectType constructor, size_t argum throw std::runtime_error("Invalid arguments when constructing 'Realm'"); } - config.path = RJSNormalizePath(config.path); - + config.path = normalize_path(config.path); ensure_directory_exists_for_file(config.path); + SharedRealm realm = realm::Realm::get_shared_realm(config); - auto delegate = new RealmDelegate(realm, JSContextGetGlobalContext(ctx)); + auto delegate = new RealmDelegate(realm, Context::get_global_context(ctx)); + if (!realm->m_binding_context) { realm->m_binding_context.reset(delegate); } + delegate->m_defaults = defaults; delegate->m_constructors = constructors; - returnObject = WrapObject(ctx, realm_class(), new SharedRealm(realm)); + + set_internal(this_object, new SharedRealm(realm)); } template -void Realm::SchemaVersion(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - using RJSAccessor = realm::NativeAccessor; - - RJSValidateArgumentRange(argumentCount, 1, 2); +void Realm::SchemaVersion(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1, 2); realm::Realm::Config config; - config.path = RJSNormalizePath(RJSValidatedStringForValue(ctx, arguments[0])); - if (argumentCount == 2) { + config.path = normalize_path(Value::validated_to_string(ctx, arguments[0])); + if (argc == 2) { auto encryptionKeyValue = arguments[1]; - std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue); + std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue); config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); } auto version = realm::Realm::get_schema_version(config); if (version == ObjectStore::NotVersioned) { - RJSSetReturnNumber(ctx, returnObject, -1); + return_value.set(-1); } else { - RJSSetReturnNumber(ctx, returnObject, version); + return_value.set(version); } } template -void Realm::GetDefaultPath(ContextType ctx, ObjectType object, ReturnType &returnObject) { - returnObject = RJSValueForString(ctx, realm::js::default_path()); +void Realm::GetDefaultPath(ContextType ctx, ObjectType object, ReturnValue &return_value) { + return_value.set(realm::js::default_path()); } template void Realm::SetDefaultPath(ContextType ctx, ObjectType object, ValueType value) { - js::set_default_path(RJSValidatedStringForValue(ctx, value, "defaultPath")); + js::set_default_path(Value::validated_to_string(ctx, value, "defaultPath")); } template -void Realm::GetPath(ContextType ctx, ObjectType object, ReturnType &returnObject) { - returnObject = RJSValueForString(ctx, RJSGetInternal(object)->get()->config().path); +void Realm::GetPath(ContextType ctx, ObjectType object, ReturnValue &return_value) { + std::string path = get_internal(object)->get()->config().path; + return_value.set(path); } - -template -void Realm::GetSchemaVersion(ContextType ctx, ObjectType object, ReturnType &returnObject) { - returnObject = JSValueMakeNumber(ctx, RJSGetInternal(object)->get()->config().schema_version); -} - -template -void Realm::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 1); - SharedRealm sharedRealm = *RJSGetInternal(thisObject); +template +void Realm::GetSchemaVersion(ContextType ctx, ObjectType object, ReturnValue &return_value) { + double version = get_internal(object)->get()->config().schema_version; + return_value.set(version); +} + +template +void Realm::Objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1); + + SharedRealm sharedRealm = *get_internal(this_object); std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); - returnObject = RJSResultsCreate(ctx, sharedRealm, className); + + // TODO +// return_value = RJSResultsCreate(ctx, sharedRealm, className); } template -void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentRange(argumentCount, 2, 3); +void Realm::Create(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 2, 3); - SharedRealm sharedRealm = *RJSGetInternal(thisObject); + SharedRealm sharedRealm = *get_internal(this_object); std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); auto &schema = sharedRealm->config().schema; auto object_schema = schema->find(className); @@ -320,53 +332,57 @@ void Realm::Create(ContextType ctx, ObjectType thisObject, size_t argumentCou throw std::runtime_error("Object type '" + className + "' not found in schema."); } - auto object = RJSValidatedValueToObject(ctx, arguments[1]); - if (RJSIsValueArray(ctx, arguments[1])) { - object = RJSDictForPropertyArray(ctx, *object_schema, object); + ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties"); + if (Value::is_array(ctx, arguments[1])) { + object = Schema::dict_for_property_array(ctx, *object_schema, object); } bool update = false; - if (argumentCount == 3) { - update = RJSValidatedValueToBoolean(ctx, arguments[2]); + if (argc == 3) { + update = Value::validated_to_boolean(ctx, arguments[2], "update"); } - returnObject = RJSObjectCreate(ctx, Object::create(ctx, sharedRealm, *object_schema, object, update)); + auto realm_object = realm::Object::create(ctx, sharedRealm, *object_schema, object, update); + return_value.set(RealmObject::create(ctx, realm_object)); } template -void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 1); +void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1); - SharedRealm realm = *RJSGetInternal(thisObject); + SharedRealm realm = *get_internal(this_object); if (!realm->is_in_transaction()) { throw std::runtime_error("Can only delete objects within a transaction."); } - auto arg = RJSValidatedValueToObject(ctx, arguments[0]); - if (RJSValueIsObjectOfClass(ctx, arg, realm::js::object_class())) { - Object *object = RJSGetInternal(arg); + ObjectType arg = Value::validated_to_object(ctx, arguments[0]); + + // TODO: fix this template call + if (Object::template is_instance(ctx, arg)) { + auto object = get_internal(arg); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); table->move_last_over(object->row().get_index()); } - else if (RJSIsValueArray(ctx, arg)) { - size_t length = RJSValidatedListLength(ctx, arg); - for (long i = length-1; i >= 0; i--) { - JSObjectRef jsObject = RJSValidatedValueToObject(ctx, RJSValidatedObjectAtIndex(ctx, arg, (unsigned int)i)); - if (!RJSValueIsObjectOfClass(ctx, jsObject, object_class())) { + else if (Value::is_array(ctx, arg)) { + uint32_t length = Object::validated_get_length(ctx, arg); + for (uint32_t i = length; i--;) { + ObjectType object = Object::validated_get_object(ctx, arg, i); + + if (!Object::template is_instance(ctx, object)) { throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); } - Object *object = RJSGetInternal(jsObject); - realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); - table->move_last_over(object->row().get_index()); + auto realm_object = get_internal(object); + realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), realm_object->get_object_schema().name); + table->move_last_over(realm_object->row().get_index()); } } - else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { - Results *results = RJSGetInternal(arg); - results->clear(); - } - else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::list_class())) { - List *list = RJSGetInternal(arg); +// else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { +// auto results = get_internal(arg); +// results->clear(); +// } + else if (Object::template is_instance(ctx, arg)) { + auto list = get_internal(arg); list->delete_all(); } else { @@ -375,10 +391,10 @@ void Realm::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCou } template -void Realm::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 0); +void Realm::DeleteAll(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); - SharedRealm realm = *RJSGetInternal(thisObject); + SharedRealm realm = *get_internal(this_object); if (!realm->is_in_transaction()) { throw std::runtime_error("Can only delete objects within a transaction."); @@ -390,14 +406,15 @@ void Realm::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argument } template -void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 1); +void Realm::Write(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1); + + SharedRealm realm = *get_internal(this_object); + FunctionType callback = Value::validated_to_function(ctx, arguments[0]); - SharedRealm realm = *RJSGetInternal(thisObject); - auto object = RJSValidatedValueToFunction(ctx, arguments[0]); try { realm->begin_transaction(); - RJSCallFunction(ctx, object, thisObject, 0, NULL); + Function::call(ctx, callback, this_object, 0, nullptr); realm->commit_transaction(); } catch (std::exception &exp) { @@ -409,42 +426,45 @@ void Realm::Write(ContextType ctx, ObjectType thisObject, size_t argumentCoun } template -void Realm::AddListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = validated_notification_name(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); +void Realm::AddListener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 2); - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->add_notification(callback); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = Value::validated_to_function(ctx, arguments[1]); + + SharedRealm realm = *get_internal(this_object); + get_delegate(realm.get())->add_notification(callback); } template -void Realm::RemoveListener(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 2); - __unused std::string name = validated_notification_name(ctx, arguments[0]); - auto callback = RJSValidatedValueToFunction(ctx, arguments[1]); +void Realm::RemoveListener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 2); - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_notification(callback); + __unused std::string name = validated_notification_name(ctx, arguments[0]); + auto callback = Value::validated_to_function(ctx, arguments[1]); + + SharedRealm realm = *get_internal(this_object); + get_delegate(realm.get())->remove_notification(callback); } template -void Realm::RemoveAllListeners(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentRange(argumentCount, 0, 1); - if (argumentCount) { +void Realm::RemoveAllListeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0, 1); + if (argc) { validated_notification_name(ctx, arguments[0]); } - SharedRealm realm = *RJSGetInternal(thisObject); - static_cast *>(realm->m_binding_context.get())->remove_all_notifications(); + SharedRealm realm = *get_internal(this_object); + get_delegate(realm.get())->remove_all_notifications(); } template -void Realm::Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject) { - RJSValidateArgumentCount(argumentCount, 0); - SharedRealm realm = *RJSGetInternal(thisObject); +void Realm::Close(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + SharedRealm realm = *get_internal(this_object); realm->close(); } -} -} \ No newline at end of file +} // js +} // realm diff --git a/src/js_results.cpp b/src/js_results.cpp index b9ac58af..0f5a4e43 100644 --- a/src/js_results.cpp +++ b/src/js_results.cpp @@ -17,8 +17,9 @@ //////////////////////////////////////////////////////////////////////////// #include "js_results.hpp" -#include "js_collection.hpp" +#include "jsc_collection.hpp" #include "js_object.hpp" +#include "jsc_util.hpp" #include "object_accessor.hpp" #include "results.hpp" #include "parser.hpp" @@ -91,7 +92,7 @@ void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAc JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { try { Results *results = RJSGetInternal(thisObject); - RJSValidateArgumentCount(argumentCount, 0); + validate_argument_count(argumentCount, 0); Results *copy = new Results(*results); copy->set_live(false); @@ -109,7 +110,7 @@ JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef JSValueRef ResultsSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { try { Results *results = RJSGetInternal(thisObject); - RJSValidateArgumentRange(argumentCount, 1, 2); + validate_argument_count(argumentCount, 1, 2); SharedRealm sharedRealm = *RJSGetInternal(thisObject); return RJSResultsCreateSorted(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments); @@ -125,7 +126,7 @@ JSValueRef ResultsSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thi JSValueRef ResultsFiltered(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { try { Results *results = RJSGetInternal(thisObject); - RJSValidateArgumentCountIsAtLeast(argumentCount, 1); + validate_argument_count_at_least(argumentCount, 1); SharedRealm sharedRealm = *RJSGetInternal(thisObject); return RJSResultsCreateFiltered(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments); @@ -189,7 +190,7 @@ JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const Ob std::vector ascending; if (RJSIsValueArray(ctx, arguments[0])) { - RJSValidateArgumentCount(argumentCount, 1, "Second argument is not allowed if passed an array of sort descriptors"); + validate_argument_count(argumentCount, 1, "Second argument is not allowed if passed an array of sort descriptors"); JSObjectRef js_prop_names = RJSValidatedValueToObject(ctx, arguments[0]); prop_count = RJSValidatedListLength(ctx, js_prop_names); @@ -214,7 +215,7 @@ JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const Ob } } else { - RJSValidateArgumentRange(argumentCount, 1, 2); + validate_argument_count(argumentCount, 1, 2); prop_count = 1; prop_names.push_back(RJSValidatedStringForValue(ctx, arguments[0])); diff --git a/src/js_results.hpp b/src/js_results.hpp index 9408e436..282d7b25 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -18,10 +18,13 @@ #pragma once -#include "js_util.hpp" #include +#include + +#include "jsc_types.hpp" namespace realm { + class ObjectSchema; class Realm; class Query; typedef std::shared_ptr SharedRealm; diff --git a/src/js_schema.cpp b/src/js_schema.cpp index ff78e108..60500f6d 100644 --- a/src/js_schema.cpp +++ b/src/js_schema.cpp @@ -19,6 +19,7 @@ #include "js_schema.hpp" #include "object_store.hpp" +/* namespace realm { struct SchemaWrapper { Schema *schema; @@ -44,3 +45,4 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { wrapper->owned = false; return js::WrapObject(ctx, RJSSchemaClass(), wrapper); } +*/ diff --git a/src/js_schema.hpp b/src/js_schema.hpp index b6b6f627..79a6aa49 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -18,62 +18,77 @@ #pragma once -#include "js_util.hpp" -#include "schema.hpp" #include -namespace realm { - class Schema; -} - -JSClassRef RJSSchemaClass(); -JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema); +#include "js_types.hpp" +#include "schema.hpp" namespace realm { namespace js { - + template -struct Schema -{ +struct Schema { using ContextType = typename T::Context; + using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using ReturnType = typename T::Return; - using StringType = typename T::String; - using ObjectDefaults = std::map; + using String = String; + using Object = Object; + using Value = Value; + + using ObjectDefaults = std::map>; using ObjectDefaultsMap = std::map; - using ConstructorMap = std::map; - - static Property parse_property(ContextType ctx, ValueType attributes, std::string propertyame, ObjectDefaults &objectDefaults); - static ObjectSchema parse_object_schema(ContextType ctx, ObjectType objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors); - static realm::Schema parse_schema(ContextType ctx, ObjectType jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors); + using ConstructorMap = std::map>; + + static ObjectType dict_for_property_array(ContextType, const ObjectSchema &, ObjectType); + static Property parse_property(ContextType, ValueType, std::string, ObjectDefaults &); + static ObjectSchema parse_object_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); + static realm::Schema parse_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); }; + +template +typename T::Object Schema::dict_for_property_array(ContextType ctx, const ObjectSchema &object_schema, ObjectType array) { + size_t count = object_schema.properties.size(); + if (count != Object::validated_get_length(ctx, array)) { + throw std::runtime_error("Array must contain values for all object properties"); + } + + ObjectType dict = Object::create_empty(ctx); + + for (uint32_t i = 0; i < count; i++) { + ValueType value = Object::get_property(ctx, array, i); + Object::set_property(ctx, dict, object_schema.properties[i].name, value); + } + + return dict; +} + template Property Schema::parse_property(ContextType ctx, ValueType attributes, std::string propertyName, ObjectDefaults &objectDefaults) { - StringType defaultString("default"); - StringType indexedString("indexed"); - StringType typeString("type"); - StringType objectTypeString("objectType"); - StringType optionalString("optional"); + static const String defaultString = "default"; + static const String indexedString = "indexed"; + static const String typeString = "type"; + static const String objectTypeString = "objectType"; + static const String optionalString = "optional"; Property prop; prop.name = propertyName; - JSObjectRef propertyObject = NULL; + ObjectType propertyObject = {}; std::string type; - if (ValueIsObject(ctx, attributes)) { - propertyObject = RJSValidatedValueToObject(ctx, attributes); - type = RJSValidatedStringProperty(ctx, propertyObject, typeString); + if (Value::is_object(ctx, attributes)) { + propertyObject = Value::validated_to_object(ctx, attributes); + type = Object::validated_get_string(ctx, propertyObject, typeString); - ValueType optionalValue = ObjectGetProperty(ctx, propertyObject, optionalString, NULL); - if (!ValueIsUndefined(ctx, optionalValue)) { - prop.is_nullable = RJSValidatedValueToBoolean(ctx, optionalValue, "'optional' designation expected to be of type boolean"); + ValueType optionalValue = Object::get_property(ctx, propertyObject, optionalString); + if (!Value::is_undefined(ctx, optionalValue)) { + prop.is_nullable = Value::validated_to_boolean(ctx, optionalValue, "optional"); } } else { - type = RJSValidatedStringForValue(ctx, attributes); + type = Value::validated_to_string(ctx, attributes); } if (type == "bool") { @@ -98,11 +113,11 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s prop.type = PropertyTypeData; } else if (type == "list") { - if (!propertyObject) { + if (!Value::is_valid(propertyObject)) { throw std::runtime_error("List property must specify 'objectType'"); } prop.type = PropertyTypeArray; - prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); + prop.object_type = Object::validated_get_string(ctx, propertyObject, objectTypeString); } else { prop.type = PropertyTypeObject; @@ -110,26 +125,25 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s // The type could either be 'object' or the name of another object type in the same schema. if (type == "object") { - if (!propertyObject) { + if (!Value::is_valid(propertyObject)) { throw std::runtime_error("Object property must specify 'objectType'"); } - prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString); + prop.object_type = Object::validated_get_string(ctx, propertyObject, objectTypeString); } else { prop.object_type = type; } } - if (propertyObject) { - ValueType defaultValue = RJSValidatedPropertyValue(ctx, propertyObject, defaultString); - if (!ValueIsUndefined(ctx, defaultValue)) { - ValueProtect(ctx, defaultValue); - objectDefaults.emplace(prop.name, defaultValue); + if (Value::is_valid(propertyObject)) { + ValueType defaultValue = Object::get_property(ctx, propertyObject, defaultString); + if (!Value::is_undefined(ctx, defaultValue)) { + objectDefaults.emplace(prop.name, Protected(ctx, defaultValue)); } - ValueType indexedValue = RJSValidatedPropertyValue(ctx, propertyObject, indexedString); - if (!ValueIsUndefined(ctx, indexedValue)) { - prop.is_indexed = RJSValidatedValueToBoolean(ctx, indexedValue); + ValueType indexedValue = Object::get_property(ctx, propertyObject, indexedString); + if (!Value::is_undefined(ctx, indexedValue)) { + prop.is_indexed = Value::validated_to_boolean(ctx, indexedValue); } } @@ -138,41 +152,41 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s template ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { - StringType nameString("name"); - StringType primaryString("primaryKey"); - StringType propertiesString("properties"); - StringType schemaString("schema"); + static const String nameString = "name"; + static const String primaryString = "primaryKey"; + static const String propertiesString = "properties"; + static const String schemaString = "schema"; - ObjectType objectConstructor = NULL; - if (ValueIsConstructor(ctx, objectSchemaObject)) { - objectConstructor = objectSchemaObject; - objectSchemaObject = RJSValidatedObjectProperty(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); + FunctionType objectConstructor = {}; + if (Value::is_constructor(ctx, objectSchemaObject)) { + objectConstructor = Value::to_constructor(ctx, objectSchemaObject); + objectSchemaObject = Object::validated_get_object(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); } ObjectDefaults objectDefaults; ObjectSchema objectSchema; - objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); + objectSchema.name = Object::validated_get_string(ctx, objectSchemaObject, nameString); - ObjectType propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); - if (RJSIsValueArray(ctx, propertiesObject)) { - size_t propertyCount = RJSValidatedListLength(ctx, propertiesObject); - for (size_t i = 0; i < propertyCount; i++) { - ObjectType propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i); - std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString); + ObjectType propertiesObject = Object::validated_get_object(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); + if (Value::is_array(ctx, propertiesObject)) { + uint32_t length = Object::validated_get_length(ctx, propertiesObject); + for (uint32_t i = 0; i < length; i++) { + ObjectType propertyObject = Object::validated_get_object(ctx, propertiesObject, i); + std::string propertyName = Object::validated_get_string(ctx, propertyObject, nameString); objectSchema.properties.emplace_back(parse_property(ctx, propertyObject, propertyName, objectDefaults)); } } else { - auto propertyNames = ObjectGetPropertyNames(ctx, propertiesObject); + auto propertyNames = Object::get_property_names(ctx, propertiesObject); for (auto propertyName : propertyNames) { - ValueType propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, StringType(propertyName.c_str())); + ValueType propertyValue = Object::get_property(ctx, propertiesObject, propertyName); objectSchema.properties.emplace_back(parse_property(ctx, propertyValue, propertyName, objectDefaults)); } } - - JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); - if (!JSValueIsUndefined(ctx, primaryValue)) { - objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue); + + ValueType primaryValue = Object::get_property(ctx, objectSchemaObject, primaryString); + if (!Value::is_undefined(ctx, primaryValue)) { + objectSchema.primary_key = Value::validated_to_string(ctx, primaryValue); Property *property = objectSchema.primary_key_property(); if (!property) { throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'"); @@ -181,9 +195,8 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc } // Store prototype so that objects of this type will have their prototype set to this prototype object. - if (objectConstructor) { - ValueProtect(ctx, objectConstructor); - constructors[objectSchema.name] = std::move(objectConstructor); + if (Value::is_valid(objectConstructor)) { + constructors.emplace(objectSchema.name, Protected(ctx, objectConstructor)); } defaults.emplace(objectSchema.name, std::move(objectDefaults)); @@ -194,14 +207,16 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc template realm::Schema Schema::parse_schema(ContextType ctx, ObjectType jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { std::vector schema; - size_t length = RJSValidatedListLength(ctx, jsonObject); - for (unsigned int i = 0; i < length; i++) { - JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i); + uint32_t length = Object::validated_get_length(ctx, jsonObject); + + for (uint32_t i = 0; i < length; i++) { + ObjectType jsonObjectSchema = Object::validated_get_object(ctx, jsonObject, i); ObjectSchema objectSchema = parse_object_schema(ctx, jsonObjectSchema, defaults, constructors); schema.emplace_back(std::move(objectSchema)); } + return realm::Schema(schema); } -} -} \ No newline at end of file +} // js +} // realm diff --git a/src/js_types.hpp b/src/js_types.hpp new file mode 100644 index 00000000..696d74bc --- /dev/null +++ b/src/js_types.hpp @@ -0,0 +1,294 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG) +# define REALM_JS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) && !(defined(DEBUG) && DEBUG) +# define REALM_JS_INLINE __forceinline +#else +# define REALM_JS_INLINE inline +#endif + +namespace realm { +namespace js { + +enum PropertyAttributes { + None = 0, + ReadOnly = 1 << 0, + DontEnum = 1 << 1, + DontDelete = 1 << 2 +}; + +template +class String { + using StringType = typename T::String; + + public: + String(const char *); + String(const StringType &); + String(StringType &&); + String(const std::string &); + + operator StringType() const; + operator std::string() const; +}; + +template +class Context { + using ContextType = typename T::Context; + using GlobalContextType = typename T::GlobalContext; + + public: + static GlobalContextType get_global_context(ContextType); +}; + +template +class Value { + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + + public: + static bool is_array(ContextType, const ValueType &); + static bool is_array_buffer(ContextType, const ValueType &); + static bool is_boolean(ContextType, const ValueType &); + static bool is_constructor(ContextType, const ValueType &); + static bool is_date(ContextType, const ValueType &); + static bool is_function(ContextType, const ValueType &); + static bool is_null(ContextType, const ValueType &); + static bool is_number(ContextType, const ValueType &); + static bool is_object(ContextType, const ValueType &); + static bool is_string(ContextType, const ValueType &); + static bool is_undefined(ContextType, const ValueType &); + static bool is_valid(const ValueType &); + + static ValueType from_boolean(ContextType, bool); + static ValueType from_null(ContextType); + static ValueType from_number(ContextType, double); + static ValueType from_string(ContextType, const String &); + static ValueType from_undefined(ContextType); + + static ObjectType to_array(ContextType, const ValueType &); + static bool to_boolean(ContextType, const ValueType &); + static FunctionType to_constructor(ContextType, const ValueType &); + static ObjectType to_date(ContextType, const ValueType &); + static FunctionType to_function(ContextType, const ValueType &); + static double to_number(ContextType, const ValueType &); + static ObjectType to_object(ContextType, const ValueType &); + static String to_string(ContextType, const ValueType &); + +#define VALIDATED(return_t, type) \ + static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \ + if (!is_##type(ctx, value)) { \ + std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \ + throw std::invalid_argument(prefix + " must be: " #type); \ + } \ + return to_##type(ctx, value); \ + } + + VALIDATED(ObjectType, array) + VALIDATED(bool, boolean) + VALIDATED(FunctionType, constructor) + VALIDATED(ObjectType, date) + VALIDATED(FunctionType, function) + VALIDATED(double, number) + VALIDATED(ObjectType, object) + VALIDATED(String, string) + +#undef VALIDATED +}; + +template +class Function { + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + + public: + static ValueType call(ContextType, const FunctionType &, const ObjectType &, uint32_t, const ValueType[]); + static ValueType call(ContextType ctx, const FunctionType &function, const ObjectType &this_object, const std::vector &arguments) { + return call(ctx, function, this_object, arguments.size(), arguments.data()); + } + + static ObjectType construct(ContextType, const FunctionType &, uint32_t, const ValueType[]); + static ValueType construct(ContextType ctx, const FunctionType &function, const std::vector &arguments) { + return construct(ctx, function, arguments.size(), arguments.data()); + } +}; + +template +class Object { + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + + public: + static bool has_property(ContextType, const ObjectType &, const String &); + static bool has_property(ContextType, const ObjectType &, uint32_t); + static ValueType get_property(ContextType, const ObjectType &, const String &); + static ValueType get_property(ContextType, const ObjectType &, uint32_t); + static void set_property(ContextType, const ObjectType &, const String &, const ValueType &, PropertyAttributes attributes = None); + static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &); + static std::vector> get_property_names(ContextType, const ObjectType &); + + static ValueType get_prototype(ContextType, const ObjectType &); + static void set_prototype(ContextType, const ObjectType &, const ValueType &); + + static uint32_t validated_get_length(ContextType ctx, const ObjectType &object) { + static const String length_string = "length"; + return Value::validated_to_number(ctx, get_property(ctx, object, length_string)); + } + +#define VALIDATED(return_t, type) \ + static return_t validated_get_##type(ContextType ctx, const ObjectType &object, const String &key, const char *message = nullptr) { \ + try { \ + return Value::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \ + } \ + catch(std::invalid_argument &e) { \ + throw message ? std::invalid_argument(message) : e; \ + } \ + } \ + static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \ + try { \ + return Value::validated_to_##type(ctx, get_property(ctx, object, index)); \ + } \ + catch(std::invalid_argument &e) { \ + throw message ? std::invalid_argument(message) : e; \ + } \ + } + + VALIDATED(ObjectType, array) + VALIDATED(bool, boolean) + VALIDATED(FunctionType, constructor) + VALIDATED(ObjectType, date) + VALIDATED(FunctionType, function) + VALIDATED(double, number) + VALIDATED(ObjectType, object) + VALIDATED(String, string) + +#undef VALIDATED + + static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, uint32_t argc, const ValueType arguments[]) { + FunctionType method = validated_get_function(ctx, object, name); + return Function::call(ctx, method, object, argc, arguments); + } + static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, const std::vector &arguments) { + return call_method(ctx, object, name, (uint32_t)arguments.size(), arguments.data()); + } + + static ObjectType create_empty(ContextType); + static ObjectType create_array(ContextType, uint32_t, const ValueType[]); + + static ObjectType create_array(ContextType ctx, const std::vector &values) { + return create_array(ctx, (uint32_t)values.size(), values.data()); + } + static ObjectType create_array(ContextType ctx) { + return create_array(ctx, 0, nullptr); + } + + static ObjectType create_date(ContextType, double); + + template + static ObjectType create(ContextType, U*); + + template + static bool is_instance(ContextType, const ObjectType &); + + template + static U* get_internal(const ObjectType &); + + template + static void set_internal(const ObjectType &, U*); +}; + +template +class Protected { + operator T() const; + bool operator==(const T &) const; + bool operator!=(const T &) const; + bool operator==(const Protected &) const; + bool operator!=(const Protected &) const; + bool operator<(const Protected &) const; +}; + +template +class Exception : public std::runtime_error { + using ContextType = typename T::Context; + using ValueType = typename T::Value; + + const Protected m_value; + + public: + Exception(ContextType ctx, const ValueType &val) + : std::runtime_error(std::string(Value::to_string(ctx, val))), m_value(ctx, val) {} + + operator ValueType() const { + return m_value; + } + + static ValueType value(ContextType ctx, const std::string &message); + + static ValueType value(ContextType ctx, const std::exception &exp) { + if (const Exception *js_exp = dynamic_cast *>(&exp)) { + return *js_exp; + } + return value(ctx, exp.what()); + } +}; + +template +class ReturnValue { + using ValueType = typename T::Value; + + public: + void set(const ValueType &); + void set(const std::string &); + void set(bool); + void set(double); + void set(int32_t); + void set(uint32_t); + void set_null(); + void set_undefined(); +}; + +template +REALM_JS_INLINE typename T::Object create_object(typename T::Context ctx, U* internal = nullptr) { + return Object::template create(ctx, internal); +} + +template +REALM_JS_INLINE U* get_internal(const typename T::Object &object) { + return Object::template get_internal(object); +} + +template +REALM_JS_INLINE void set_internal(const typename T::Object &object, U* ptr) { + Object::template set_internal(object, ptr); +} + +} // js +} // realm diff --git a/src/js_util.cpp b/src/js_util.cpp deleted file mode 100644 index 905a1f14..00000000 --- a/src/js_util.cpp +++ /dev/null @@ -1,99 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_util.hpp" -#include - -using namespace realm; - -JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp) { - JSValueRef value = exp.exception(); - return JSObjectMakeError(ctx, 1, &value, NULL); -} - -JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp) { - if (RJSException *rjsExp = dynamic_cast(&exp)) { - return RJSMakeError(ctx, *rjsExp); - } - return RJSMakeError(ctx, exp.what()); -} - -JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message) { - JSValueRef value = RJSValueForString(ctx, message); - return JSObjectMakeError(ctx, 1, &value, NULL); -} - -std::string RJSStringForJSString(JSStringRef jsString) { - std::string str; - size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString); - str.resize(maxSize); - str.resize(JSStringGetUTF8CString(jsString, &str[0], maxSize) - 1); - return str; -} - -std::string RJSStringForValue(JSContextRef ctx, JSValueRef value) { - JSValueRef exception = nullptr; - JSStringRef jsString = JSValueToStringCopy(ctx, value, &exception); - if (!jsString) { - throw RJSException(ctx, exception); - } - - std::string string = RJSStringForJSString(jsString); - JSStringRelease(jsString); - - return string; -} - -std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name) { - if (!JSValueIsString(ctx, value)) { - if (name) { - throw std::invalid_argument((std::string)"'" + name + "' must be of type 'String'"); - } - else { - throw std::invalid_argument("JSValue must be of type 'String'"); - } - } - - return RJSStringForValue(ctx, value); -} - -JSStringRef RJSStringForString(const std::string &str) { - return JSStringCreateWithUTF8CString(str.c_str()); -} - -JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str) { - JSStringRef jsStr = RJSStringForString(str); - JSValueRef value = JSValueMakeString(ctx, jsStr); - JSStringRelease(jsStr); - return value; -} - -bool RJSIsValueArray(JSContextRef ctx, JSValueRef value) { - static JSStringRef arrayString = JSStringCreateWithUTF8CString("Array"); - return RJSIsValueObjectOfType(ctx, value, arrayString); -} - -bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value) { - static JSStringRef arrayString = JSStringCreateWithUTF8CString("ArrayBuffer"); - return RJSIsValueObjectOfType(ctx, value, arrayString); -} - -bool RJSIsValueDate(JSContextRef ctx, JSValueRef value) { - static JSStringRef dateString = JSStringCreateWithUTF8CString("Date"); - return RJSIsValueObjectOfType(ctx, value, dateString); -} diff --git a/src/js_util.hpp b/src/js_util.hpp index ce7d1388..4a51fe51 100644 --- a/src/js_util.hpp +++ b/src/js_util.hpp @@ -18,257 +18,25 @@ #pragma once -#include -#include -#include - -#include -#include +#include #include - #include -#include -#include "property.hpp" -#include "js_compat.hpp" -#include "schema.hpp" - -#define WRAP_EXCEPTION(METHOD, EXCEPTION, ARGS...) \ -try { METHOD(ARGS); } \ -catch(std::exception &e) { RJSSetException(ctx, EXCEPTION, e); } - -#define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnValue); \ - return returnValue; \ -} - -#define WRAP_CONSTRUCTOR(CLASS_NAME, METHOD_NAME) \ -JSObjectRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ - JSObjectRef returnObject = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, constructor, argumentCount, arguments, returnObject); \ - return returnObject; \ -} - -#define WRAP_PROPERTY_GETTER(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, returnValue); \ - return returnValue; \ -} - -#define WRAP_PROPERTY_SETTER(CLASS_NAME, METHOD_NAME) \ -bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, value); \ - return true; \ -} - -// for stol failure (std::invalid_argument) this could be another property that is handled externally, so ignore -#define WRAP_INDEXED_GETTER(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - try { \ - size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ - CLASS_NAME::METHOD_NAME(ctx, object, index, returnValue); return returnValue; \ - } \ - catch (std::out_of_range &exp) { return JSValueMakeUndefined(ctx); } \ - catch (std::invalid_argument &exp) { return NULL; } \ - catch (std::exception &e) { RJSSetException(ctx, *ex, e); return NULL; } \ -} - -#define WRAP_INDEXED_SETTER(CLASS_NAME, METHOD_NAME) \ -bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ - try { \ - size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ - { CLASS_NAME::METHOD_NAME(ctx, object, index, value); return true; } \ - } \ - catch (std::out_of_range &exp) { RJSSetException(ctx, *ex, exp); } \ - catch (std::invalid_argument &exp) { *ex = RJSMakeError(ctx, "Invalid index"); } \ - catch (std::exception &e) { RJSSetException(ctx, *ex, e); } \ - return false; \ -} +#include "shared_realm.hpp" +namespace realm { +namespace js { template -inline void RJSFinalize(JSObjectRef object) { - delete static_cast(JSObjectGetPrivate(object)); - JSObjectSetPrivate(object, NULL); +class RealmDelegate; + +template +static inline RealmDelegate *get_delegate(Realm *realm) { + return static_cast *>(realm->m_binding_context.get()); } template -inline T RJSGetInternal(JSObjectRef jsObject) { - return static_cast(JSObjectGetPrivate(jsObject)); -} - -template -JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL, - JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL, const JSStaticValue *values = NULL) { - JSClassDefinition classDefinition = kJSClassDefinitionEmpty; - classDefinition.className = name; - classDefinition.finalize = RJSFinalize; - classDefinition.getProperty = getter; - classDefinition.setProperty = setter; - classDefinition.staticFunctions = funcs; - classDefinition.getPropertyNames = propertyNames; - classDefinition.parentClass = parentClass; - classDefinition.staticValues = values; - return JSClassCreate(&classDefinition); -} - -std::string RJSStringForJSString(JSStringRef jsString); -std::string RJSStringForValue(JSContextRef ctx, JSValueRef value); -std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name = nullptr); - -JSStringRef RJSStringForString(const std::string &str); -JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str); - -inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected, const char *message = nullptr) { - if (argumentCount != expected) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - -inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected, const char *message = nullptr) { - if (argumentCount < expected) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - -inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max, const char *message = nullptr) { - if (argumentCount < min || argumentCount > max) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - -class RJSException : public std::runtime_error { -public: - RJSException(JSContextRef ctx, JSValueRef &ex) : std::runtime_error(RJSStringForValue(ctx, ex)), - m_jsException(ex) {} - JSValueRef exception() { return m_jsException; } - -private: - JSValueRef m_jsException; -}; - -JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp); -JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp); -JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message); - -bool RJSIsValueArray(JSContextRef ctx, JSValueRef value); -bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value); -bool RJSIsValueDate(JSContextRef ctx, JSValueRef value); - -static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - JSObjectRef object = JSValueToObject(ctx, value, NULL); - if (!object) { - throw std::runtime_error(message ?: "Value is not an object."); - } - return object; -} - -static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - JSObjectRef object = JSValueToObject(ctx, value, NULL); - if (!object || !RJSIsValueDate(ctx, object)) { - throw std::runtime_error(message ?: "Value is not a date."); - } - return object; -} - -static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - 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) { - if (JSValueIsNull(ctx, value)) { - throw std::invalid_argument("`null` is not a number."); - } - - JSValueRef exception = NULL; - double number = JSValueToNumber(ctx, value, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - if (isnan(number)) { - throw std::invalid_argument("Value not convertible to a number."); - } - return number; -} - -static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value, const char *err = nullptr) { - if (!JSValueIsBoolean(ctx, value)) { - throw std::invalid_argument(err ?: "Value is not a boolean."); - } - return JSValueToBoolean(ctx, value); -} - -static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return propertyValue; -} - -static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return propertyValue; -} - -static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = nullptr) { - JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); - if (JSValueIsUndefined(ctx, propertyValue)) { - throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined"); - } - return RJSValidatedValueToObject(ctx, propertyValue, err); -} - -static inline JSObjectRef RJSValidatedObjectAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { - return RJSValidatedValueToObject(ctx, RJSValidatedPropertyAtIndex(ctx, object, index)); -} - -static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return RJSValidatedStringForValue(ctx, propertyValue, RJSStringForJSString(property).c_str()); -} - -static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object) { - JSValueRef exception = NULL; - static JSStringRef lengthString = JSStringCreateWithUTF8CString("length"); - JSValueRef lengthValue = JSObjectGetProperty(ctx, object, lengthString, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - if (!JSValueIsNumber(ctx, lengthValue)) { - throw std::runtime_error("Missing property 'length'"); - } - - return RJSValidatedValueToNumber(ctx, lengthValue); -} - -static inline void RJSValidatedSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes = 0) { - JSValueRef exception = NULL; - JSObjectSetProperty(ctx, object, propertyName, value, attributes, &exception); - if (exception) { - throw RJSException(ctx, exception); - } -} - -template -T stot(const std::string s) { +static inline T stot(const std::string &s) { std::istringstream iss(s); T value; iss >> value; @@ -278,82 +46,34 @@ T stot(const std::string s) { return value; } -static inline size_t RJSValidatedPositiveIndex(std::string indexStr) { - long index = stot(indexStr); +static inline uint32_t validated_positive_index(std::string string) { + int64_t index = stot(string); if (index < 0) { - throw std::out_of_range(std::string("Index ") + indexStr + " cannot be less than zero."); + throw std::out_of_range(std::string("Index ") + string + " cannot be less than zero."); } - return index; -} - -static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JSStringRef type) { - JSObjectRef globalObject = JSContextGetGlobalObject(ctx); - - JSValueRef exception = NULL; - JSValueRef constructorValue = JSObjectGetProperty(ctx, globalObject, type, &exception); - if (exception) { - throw RJSException(ctx, exception); + if (index > std::numeric_limits::max()) { + throw std::out_of_range(std::string("Index ") + string + " must be a 32-bit unsigned integer"); } - - bool ret = JSValueIsInstanceOfConstructor(ctx, value, RJSValidatedValueToObject(ctx, constructorValue), &exception); - if (exception) { - throw RJSException(ctx, exception); - } - - return ret; + return static_cast(index); } -static inline void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { - returnObject = JSValueMakeUndefined(ctx); -} - -template -static inline void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { - returnObject = JSValueMakeNumber(ctx, number); -} - -static inline void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { - returnObject = JSObjectMakeArray(ctx, count, objects, NULL); -} - -static inline void RJSSetException(JSContextRef ctx, JSValueRef &exceptionObject, std::exception &exception) { - exceptionObject = RJSMakeError(ctx, exception); -} - -static JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const realm::ObjectSchema &object_schema, JSObjectRef array) { - // copy to dictionary - if (object_schema.properties.size() != RJSValidatedListLength(ctx, array)) { - throw std::runtime_error("Array must contain values for all object properties"); - } - - JSValueRef exception = NULL; - JSObjectRef dict = JSObjectMake(ctx, NULL, NULL); - for (unsigned int i = 0; i < object_schema.properties.size(); i++) { - JSStringRef nameStr = JSStringCreateWithUTF8CString(object_schema.properties[i].name.c_str()); - JSValueRef value = JSObjectGetPropertyAtIndex(ctx, array, i, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSObjectSetProperty(ctx, dict, nameStr, value, kJSPropertyAttributeNone, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - JSStringRelease(nameStr); - } - return dict; -} - -static void RJSCallFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef *arguments) { - JSValueRef exception = NULL; - JSObjectCallAsFunction(ctx, function, object, argumentCount, arguments, &exception); - if (exception) { - throw RJSException(ctx, exception); +static inline void validate_argument_count(size_t count, size_t expected, const char *message = nullptr) { + if (count != expected) { + throw std::invalid_argument(message ?: "Invalid arguments"); } } - -static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) { - return JSValueIsObjectOfClass(ctx, value, jsClass); +static inline void validate_argument_count(size_t count, size_t min, size_t max, const char *message = nullptr) { + if (count < min || count > max) { + throw std::invalid_argument(message ?: "Invalid arguments"); + } } +static inline void validate_argument_count_at_least(size_t count, size_t expected, const char *message = nullptr) { + if (count < expected) { + throw std::invalid_argument(message ?: "Invalid arguments"); + } +} +} // js +} // realm diff --git a/src/jsc/js_compat.hpp b/src/jsc/js_compat.hpp deleted file mode 100644 index ecdd5e6a..00000000 --- a/src/jsc/js_compat.hpp +++ /dev/null @@ -1,83 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "types.hpp" -#include -#include - -namespace realm { -namespace js { - -static inline bool ValueIsUndefined(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsUndefined(ctx, value); } -static inline bool ValueIsNull(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNull(ctx, value); } -static inline bool ValueIsBoolean(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsBoolean(ctx, value); } -static inline bool ValueIsNumber(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsNumber(ctx, value); } -static inline bool ValueIsString(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsString(ctx, value); } -static inline bool ValueIsObject(jsc::Types::Context ctx, jsc::Types::Value value) { return JSValueIsObject(ctx, value); } -static inline bool ValueIsConstructor(jsc::Types::Context ctx, jsc::Types::Value value) { return ValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); } - -static inline jsc::Types::Object ValueToObject(jsc::Types::Context ctx, jsc::Types::Value value) { return (JSObjectRef)value; } - -static inline void ValueProtect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueProtect(ctx, value); } -static inline void ValueUnprotect(jsc::Types::Context ctx, jsc::Types::Value value) { JSValueUnprotect(ctx, value); } - -static inline std::string StringTypeToString(JSStringRef jsString) { - std::string str; - size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString); - str.resize(maxSize); - str.resize(JSStringGetUTF8CString(jsString, &str[0], maxSize) - 1); - return str; -} - -static inline std::vector ObjectGetPropertyNames(jsc::Types::Context ctx, jsc::Types::Object object) { - JSPropertyNameArrayRef propertyNames = JSObjectCopyPropertyNames(ctx, object); - size_t propertyCount = JSPropertyNameArrayGetCount(propertyNames); - std::vector outNames; - for (size_t i = 0; i < propertyCount; i++) { - outNames.push_back(StringTypeToString(JSPropertyNameArrayGetNameAtIndex(propertyNames, i))); - } - JSPropertyNameArrayRelease(propertyNames); - return outNames; -} -static inline jsc::Types::Value ObjectGetProperty(jsc::Types::Context ctx, jsc::Types::Object object, jsc::Types::String propertyName, jsc::Types::Exception *exception) { - return JSObjectGetProperty(ctx, object, propertyName, exception); -} -static inline void ObjectCallAsFunction(jsc::Types::Context ctx, jsc::Types::Object function, jsc::Types::Object thisObject, size_t argsCount, const jsc::Types::Value args[], jsc::Types::Exception &exception) { - JSObjectCallAsFunction(ctx, function, thisObject, argsCount, args, &exception); -} - -static inline void GlobalContextProtect(jsc::Types::GlobalContext ctx) { JSGlobalContextRetain(ctx); } -static inline void GlobalContextUnprotect(jsc::Types::GlobalContext ctx) { JSGlobalContextRelease(ctx); } - -template -jsc::Types::Object WrapObject(jsc::Types::Context ctx, jsc::Types::ObjectClass objectClass, T internal, jsc::Types::Object prototype = nullptr) { - JSObjectRef ref = JSObjectMake(ctx, objectClass, (void *)internal); - if (prototype) { - JSObjectSetPrototype(ctx, ref, prototype); - } - return ref; -} - -jsc::Types::ObjectClass realm_class(); -jsc::Types::ObjectClass list_class(); -jsc::Types::ObjectClass object_class(); -jsc::Types::ObjectClass results_class(); - -}} diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp new file mode 100644 index 00000000..ae577531 --- /dev/null +++ b/src/jsc/jsc_class.hpp @@ -0,0 +1,309 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" +#include "js_class.hpp" +#include "js_util.hpp" + +namespace realm { +namespace jsc { + +template +using ObjectClass = js::ObjectClass; + +using BaseObjectClass = js::BaseObjectClass; +using ConstructorType = js::ConstructorType; +using MethodType = js::MethodType; +using PropertyGetterType = js::PropertyGetterType; +using PropertySetterType = js::PropertySetterType; +using IndexPropertyGetterType = js::IndexPropertyGetterType; +using IndexPropertySetterType = js::IndexPropertySetterType; +using StringPropertyGetterType = js::StringPropertyGetterType; +using StringPropertySetterType = js::StringPropertySetterType; +using StringPropertyEnumeratorType = js::StringPropertyEnumeratorType; + +template +static inline T stot(const std::string &s) { + std::istringstream iss(s); + T value; + iss >> value; + if (iss.fail()) { + throw std::invalid_argument("Cannot convert string '" + s + "'"); + } + return value; +} + +template +class ObjectWrap { + static ObjectClass s_class; + + std::unique_ptr m_object; + + ObjectWrap(T* object = nullptr) : m_object(object) {} + + static JSObjectRef construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { + JSObjectRef this_object = ObjectWrap::create(ctx, nullptr); + try { + s_class.constructor(ctx, this_object, argc, arguments); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return this_object; + } + + static JSValueRef get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { + if (auto index_getter = s_class.index_accessor.getter) { + try { + uint32_t index = validated_positive_index(jsc::String(property)); + return index_getter(ctx, object, index, exception); + } + catch (std::out_of_range &) {} + catch (std::invalid_argument &) {} + } + if (auto string_getter = s_class.string_accessor.getter) { + return string_getter(ctx, object, property, exception); + } + return NULL; + } + + static bool set_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { + if (auto index_setter = s_class.index_accessor.setter) { + try { + uint32_t index = validated_positive_index(jsc::String(property)); + return index_setter(ctx, object, index, value, exception); + } + catch (std::out_of_range &) {} + catch (std::invalid_argument &) {} + } + if (auto string_setter = s_class.string_accessor.setter) { + return string_setter(ctx, object, property, value, exception); + } + return false; + } + + static void get_property_names(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { + if (s_class.index_accessor.getter) { + try { + uint32_t length = Object::validated_get_length(ctx, object); + char string[32]; + for (uint32_t i = 0; i < length; i++) { + sprintf(string, "%lu", i); + JSPropertyNameAccumulatorAddName(accumulator, jsc::String(string)); + } + } + catch (std::exception &) { + // Enumerating properties should never throw an exception into JS. + } + } + if (auto string_enumerator = s_class.string_accessor.enumerator) { + string_enumerator(ctx, object, accumulator); + } + } + + template + static JSClassRef get_superclass(BaseObjectClass*) { + return nullptr; + } + template + static JSClassRef get_superclass(ObjectClass*) { + return ObjectWrap::get_class(); + } + + static JSClassRef create_class() { + JSStaticFunction staticFunctions[s_class.methods.size() + 1]; + JSStaticValue staticValues[s_class.properties.size() + 1]; + JSClassDefinition definition; + + // TODO: Set parentClass ensuring finalize is setup to do the right thing. + definition.finalize = finalize; + + if (s_class.constructor) { + // TODO: Correctly setup constructor class with static methods/properties. + // definition.callAsConstructor = construct; + } + if (s_class.index_accessor.getter || s_class.string_accessor.getter) { + definition.getProperty = get_property; + } + if (s_class.index_accessor.setter || s_class.string_accessor.setter) { + definition.setProperty = set_property; + } + if (s_class.index_accessor.getter || s_class.string_accessor.enumerator) { + definition.getPropertyNames = get_property_names; + } + + if (!s_class.methods.empty()) { + JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : s_class.methods) { + staticFunctions[index++] = {pair.first.c_str(), pair.second, attributes}; + } + + staticFunctions[index] = {0}; + definition.staticFunctions = staticFunctions; + } + + if (!s_class.properties.empty()) { + JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : s_class.properties) { + auto &prop = pair.second; + staticValues[index++] = {pair.first.c_str(), prop.getter, prop.setter, attributes | (prop.setter ? 0 : kJSPropertyAttributeReadOnly)}; + } + + staticValues[index] = {0}; + definition.staticValues = staticValues; + } + + return JSClassCreate(&definition); + } + + static JSClassRef get_class() { + static JSClassRef js_class = create_class(); + return js_class; + } + + static void finalize(JSObjectRef object) { + delete static_cast *>(JSObjectGetPrivate(object)); + } + + static JSObjectRef uncallable_constructor(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { + *exception = jsc::Exception::value(ctx, "Illegal constructor"); + return NULL; + } + + public: + operator T*() const { + return m_object.get(); + } + + static JSObjectRef create(JSContextRef ctx, T* internal = nullptr) { + return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); + } + + static JSObjectRef create_constructor(JSContextRef ctx) { + return JSObjectMakeConstructor(ctx, get_class(), uncallable_constructor); + } + + static bool has_instance(JSContextRef ctx, JSValueRef value) { + return JSValueIsObjectOfClass(ctx, value, get_class()); + } +}; + +// The declared static variables must be defined as well. +template ObjectClass ObjectWrap::s_class; + +} // jsc + +namespace js { + +template +JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { + jsc::ReturnValue return_value(ctx); + try { + F(ctx, this_object, argc, arguments, return_value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return return_value; +} + +template +JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { + jsc::ReturnValue return_value(ctx); + try { + F(ctx, object, return_value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return return_value; +} + +template +void wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { + try { + F(ctx, object, value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } +} + +template +JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef* exception) { + jsc::ReturnValue return_value(ctx); + try { + F(ctx, object, index, return_value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return return_value; +} + +template +bool wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef value, JSValueRef* exception) { + try { + return F(ctx, object, index, value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return false; +} + +template +JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { + jsc::ReturnValue return_value(ctx); + try { + F(ctx, object, property, return_value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return return_value; +} + +template +bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { + try { + return F(ctx, object, property, value); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return false; +} + +template +void wrap(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { + auto names = F(ctx, object); + for (auto &name : names) { + JSPropertyNameAccumulatorAddName(accumulator, name); + } +} + +} // js +} // realm + diff --git a/src/js_collection.cpp b/src/jsc/jsc_collection.cpp similarity index 97% rename from src/js_collection.cpp rename to src/jsc/jsc_collection.cpp index 968404f2..6af2b2a5 100644 --- a/src/js_collection.cpp +++ b/src/jsc/jsc_collection.cpp @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////// -#include "js_collection.hpp" +#include "jsc_collection.hpp" static JSClassRef RJSCreateCollectionClass() { JSClassDefinition classDefinition = kJSClassDefinitionEmpty; diff --git a/src/js_collection.hpp b/src/jsc/jsc_collection.hpp similarity index 96% rename from src/js_collection.hpp rename to src/jsc/jsc_collection.hpp index ee21c391..f871a1c4 100644 --- a/src/js_collection.hpp +++ b/src/jsc/jsc_collection.hpp @@ -18,6 +18,6 @@ #pragma once -#include "js_util.hpp" +#include "jsc_types.hpp" JSClassRef RJSCollectionClass(); diff --git a/src/js_init.cpp b/src/jsc/jsc_init.cpp similarity index 66% rename from src/js_init.cpp rename to src/jsc/jsc_init.cpp index 940ba9a6..537ce572 100644 --- a/src/js_init.cpp +++ b/src/jsc/jsc_init.cpp @@ -16,27 +16,28 @@ // //////////////////////////////////////////////////////////////////////////// -#include "js_init.h" +#include +#include + +#include "jsc_init.hpp" #include "jsc_realm.hpp" -#include "js_object.hpp" -#include "js_collection.hpp" +#include "jsc_collection.hpp" #include "jsc_list.hpp" -#include "js_results.hpp" -#include "js_util.hpp" -#include "js_schema.hpp" +#include "jsc_util.hpp" #include "platform.hpp" #include "shared_realm.hpp" #include "impl/realm_coordinator.hpp" -#include -#include extern "C" { +using namespace realm; +using namespace realm::jsc; + JSValueRef RJSTypeGet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - std::string str = RJSStringForJSString(propertyName); + std::string str = String(propertyName); std::transform(str.begin(), str.end(), str.begin(), ::tolower); - return RJSValueForString(ctx, str); + return Value::from_string(ctx, str); } JSClassRef RJSRealmTypeClass() { @@ -58,54 +59,49 @@ JSClassRef RJSRealmTypeClass() { return JSClassCreate(&realmTypesDefinition); } -static JSObjectRef UncallableConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - *exception = RJSMakeError(ctx, "Illegal constructor"); - return NULL; -} - static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { RJSClearTestState(); return NULL; } JSObjectRef RJSConstructorCreate(JSContextRef ctx) { - static JSStringRef clearTestStateString = JSStringCreateWithUTF8CString("clearTestState"); - static JSStringRef collectionString = JSStringCreateWithUTF8CString("Collection"); - static JSStringRef listString = JSStringCreateWithUTF8CString("List"); - static JSStringRef resultsString = JSStringCreateWithUTF8CString("Results"); - static JSStringRef typeString = JSStringCreateWithUTF8CString("Types"); + static const String clearTestStateString = "clearTestState"; + static const String collectionString = "Collection"; + static const String listString = "List"; + static const String resultsString = "Results"; + static const String typeString = "Types"; JSObjectRef realmObject = JSObjectMake(ctx, RJSRealmConstructorClass(), NULL); - JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + js::PropertyAttributes attributes = js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete); - JSObjectRef collectionConstructor = JSObjectMakeConstructor(ctx, RJSCollectionClass(), UncallableConstructor); - RJSValidatedSetProperty(ctx, realmObject, collectionString, collectionConstructor, attributes); +// JSObjectRef collectionConstructor = JSObjectMakeConstructor(ctx, RJSCollectionClass(), UncallableConstructor); +// RJSValidatedSetProperty(ctx, realmObject, collectionString, collectionConstructor, attributes); - JSObjectRef listConstructor = JSObjectMakeConstructor(ctx, RJSListClass(), UncallableConstructor); - RJSValidatedSetProperty(ctx, realmObject, listString, listConstructor, attributes); +// JSObjectRef listConstructor = JSObjectMakeConstructor(ctx, RJSListClass(), UncallableConstructor); +// RJSValidatedSetProperty(ctx, realmObject, listString, listConstructor, attributes); - JSObjectRef resultsContructor = JSObjectMakeConstructor(ctx, RJSResultsClass(), UncallableConstructor); - RJSValidatedSetProperty(ctx, realmObject, resultsString, resultsContructor, attributes); +// JSObjectRef resultsContructor = JSObjectMakeConstructor(ctx, RJSResultsClass(), UncallableConstructor); +// RJSValidatedSetProperty(ctx, realmObject, resultsString, resultsContructor, attributes); JSObjectRef typesObject = JSObjectMake(ctx, RJSRealmTypeClass(), NULL); RJSValidatedSetProperty(ctx, realmObject, typeString, typesObject, attributes); JSObjectRef clearTestStateFunction = JSObjectMakeFunctionWithCallback(ctx, clearTestStateString, ClearTestState); - RJSValidatedSetProperty(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes); + jsc::Object::set_property(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes); return realmObject; } void RJSInitializeInContext(JSContextRef ctx) { + static const String nameString = "Realm"; + JSObjectRef globalObject = JSContextGetGlobalObject(ctx); JSObjectRef realmObject = RJSConstructorCreate(ctx); JSValueRef exception = NULL; - JSStringRef nameString = JSStringCreateWithUTF8CString("Realm"); JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; JSObjectSetProperty(ctx, globalObject, nameString, realmObject, attributes, &exception); - JSStringRelease(nameString); assert(!exception); } diff --git a/src/js_init.h b/src/jsc/jsc_init.h similarity index 100% rename from src/js_init.h rename to src/jsc/jsc_init.h diff --git a/src/jsc/jsc_init.hpp b/src/jsc/jsc_init.hpp new file mode 100644 index 00000000..aa47aa82 --- /dev/null +++ b/src/jsc/jsc_init.hpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_init.h" +#include "jsc_object_accessor.hpp" diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp index 347c8688..232f51fc 100644 --- a/src/jsc/jsc_list.cpp +++ b/src/jsc/jsc_list.cpp @@ -16,7 +16,10 @@ // //////////////////////////////////////////////////////////////////////////// +#include "jsc_class.hpp" #include "jsc_list.hpp" +#include "jsc_collection.hpp" +#include "jsc_util.hpp" #include "js_list.hpp" #include @@ -25,7 +28,7 @@ using RJSAccessor = realm::NativeAccessor; using namespace realm; void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { - List *list = RJSGetInternal(object); + List *list = js::get_internal(object); size_t size = list->size(); char str[32]; @@ -37,7 +40,7 @@ void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccum } } -using RJSList = realm::js::List; +using RJSList = js::List; WRAP_PROPERTY_GETTER(RJSList, GetLength) WRAP_INDEXED_GETTER(RJSList, GetIndex) WRAP_INDEXED_SETTER(RJSList, SetIndex) diff --git a/src/jsc/jsc_list.hpp b/src/jsc/jsc_list.hpp index e5592661..df11a017 100644 --- a/src/jsc/jsc_list.hpp +++ b/src/jsc/jsc_list.hpp @@ -18,7 +18,7 @@ #pragma once -#include "js_util.hpp" +#include "jsc_types.hpp" #include "list.hpp" JSClassRef RJSListClass(); diff --git a/src/jsc/jsc_object_accessor.cpp b/src/jsc/jsc_object_accessor.cpp new file mode 100644 index 00000000..99c46832 --- /dev/null +++ b/src/jsc/jsc_object_accessor.cpp @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "jsc_object_accessor.hpp" + +using namespace realm; +using namespace realm::jsc; + +using Accessor = js::NativeAccessor; + +template<> +std::string Accessor::to_binary(JSContextRef ctx, JSValueRef &value) { + static jsc::String s_array_buffer = "ArrayBuffer"; + static jsc::String s_buffer = "buffer"; + static jsc::String s_byte_length = "byteLength"; + static jsc::String s_byte_offset = "byteOffset"; + static jsc::String s_is_view = "isView"; + static jsc::String s_uint8_array = "Uint8Array"; + + JSObjectRef global_object = JSContextGetGlobalObject(ctx); + JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_uint8_array); + JSValueRef uint8_array_arguments[3]; + uint32_t uint8_array_argc = 0; + + // Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView). + if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) { + uint8_array_arguments[0] = value; + uint8_array_argc = 1; + } + else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) { + // Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val). + JSValueRef is_view = jsc::Object::call_method(ctx, array_buffer_constructor, s_is_view, 1, &object); + + if (jsc::Value::to_boolean(ctx, is_view)) { + uint8_array_arguments[0] = jsc::Object::validated_get_object(ctx, object, s_buffer); + uint8_array_arguments[1] = jsc::Object::get_property(ctx, object, s_byte_offset); + uint8_array_arguments[2] = jsc::Object::get_property(ctx, object, s_byte_length); + uint8_array_argc = 3; + } + } + + if (!uint8_array_argc) { + throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary"); + } + + JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments); + uint32_t byte_count = jsc::Object::validated_get_length(ctx, uint8_array); + std::string bytes(byte_count, 0); + + for (uint32_t i = 0; i < byte_count; i++) { + JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i); + bytes[i] = jsc::Value::to_number(ctx, byteValue); + } + + return bytes; +} + +template<> +JSValueRef Accessor::from_binary(JSContextRef ctx, BinaryData data) { + static jsc::String s_buffer = "buffer"; + static jsc::String s_uint8_array = "Uint8Array"; + + size_t byte_count = data.size(); + JSValueRef byte_count_value = jsc::Value::from_number(ctx, byte_count); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, JSContextGetGlobalObject(ctx), s_uint8_array); + JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, 1, &byte_count_value); + + for (uint32_t i = 0; i < byte_count; i++) { + JSValueRef num = jsc::Value::from_number(ctx, data[i]); + jsc::Object::set_property(ctx, uint8_array, i, num); + } + + return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer); +} diff --git a/src/jsc/types.hpp b/src/jsc/jsc_object_accessor.hpp similarity index 50% rename from src/jsc/types.hpp rename to src/jsc/jsc_object_accessor.hpp index 829e2953..b93e6d9e 100644 --- a/src/jsc/types.hpp +++ b/src/jsc/jsc_object_accessor.hpp @@ -18,34 +18,13 @@ #pragma once -#include -#include -#include +#include "jsc_class.hpp" +#include "js_object_accessor.hpp" namespace realm { -namespace jsc { + +// Specialize a native accessor class for JSC. +template<> +class NativeAccessor : public js::NativeAccessor {}; -class String { - public: - String(const char * str) : m_str(JSStringCreateWithUTF8CString(str)) {} - String(const String &other) : m_str(JSStringRetain(other)) {} - ~String() { JSStringRelease(m_str); } - operator JSStringRef() const { return m_str; } - - private: - JSStringRef m_str; -}; - -struct Types { - using Context = JSContextRef; - using GlobalContext = JSGlobalContextRef; - using ObjectClass = JSClassRef; - using Value = JSValueRef; - using Object = JSObjectRef; - using String = jsc::String; - using Function = JSObjectRef; - using Return = JSValueRef; - using Exception = JSValueRef; -}; - -}} \ No newline at end of file +} // realm diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp index 118bf267..f23a74ce 100644 --- a/src/jsc/jsc_realm.cpp +++ b/src/jsc/jsc_realm.cpp @@ -16,12 +16,14 @@ // //////////////////////////////////////////////////////////////////////////// +#include "jsc_class.hpp" #include "jsc_realm.hpp" #include "js_realm.hpp" #include "js_object.hpp" #include "js_results.hpp" #include "jsc_list.hpp" #include "js_schema.hpp" +#include "jsc_util.hpp" #include "platform.hpp" #include "shared_realm.hpp" @@ -79,8 +81,8 @@ WRAP_PROPERTY_GETTER(RJSRealm, GetPath) WRAP_PROPERTY_GETTER(RJSRealm, GetSchemaVersion) static const JSStaticValue RealmStaticProperties[] = { - {"path", RJSRealmGetPath, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"schemaVersion", RJSRealmGetSchemaVersion, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"path", RJSRealmGetPath, NULL, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"schemaVersion", RJSRealmGetSchemaVersion, NULL, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL} }; diff --git a/src/jsc/jsc_realm.hpp b/src/jsc/jsc_realm.hpp index ea5dbabc..b08aae73 100644 --- a/src/jsc/jsc_realm.hpp +++ b/src/jsc/jsc_realm.hpp @@ -18,7 +18,7 @@ #pragma once -#include "types.hpp" +#include "jsc_types.hpp" JSClassRef RJSRealmClass(); JSClassRef RJSRealmConstructorClass(); diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp new file mode 100644 index 00000000..e7346404 --- /dev/null +++ b/src/jsc/jsc_types.hpp @@ -0,0 +1,502 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +#include + +#include "js_types.hpp" + +namespace realm { +namespace jsc { + +struct Types { + using Context = JSContextRef; + using GlobalContext = JSGlobalContextRef; + using ObjectClass = JSClassRef; + using Value = JSValueRef; + using Object = JSObjectRef; + using String = JSStringRef; + using Function = JSObjectRef; + + using ConstructorCallback = JSObjectCallAsConstructorCallback; + using FunctionCallback = JSObjectCallAsFunctionCallback; + using PropertyGetterCallback = JSObjectGetPropertyCallback; + using PropertySetterCallback = JSObjectSetPropertyCallback; + using IndexPropertyGetterCallback = JSValueRef(JSContextRef, JSObjectRef, uint32_t, JSValueRef*); + using IndexPropertySetterCallback = bool(JSContextRef, JSObjectRef, uint32_t, JSValueRef, JSValueRef*); + using StringPropertyGetterCallback = JSObjectGetPropertyCallback; + using StringPropertySetterCallback = JSObjectSetPropertyCallback; +}; + +template +class Protected { + const T m_value; + + public: + Protected(T value) : m_value(value) {} + + operator T() const { + return m_value; + } + bool operator==(const T &other) const { + return m_value == other; + } + bool operator!=(const T &other) const { + return m_value != other; + } + bool operator==(const Protected &other) const { + return m_value == other; + } + bool operator!=(const Protected &other) const { + return m_value != other; + } + bool operator<(const Protected &other) const { + return m_value < other.m_value; + } +}; + +template +class ObjectWrap; + +using String = js::String; +using Context = js::Context; +using Value = js::Value; +using Function = js::Function; +using Object = js::Object; +using Exception = js::Exception; +using ReturnValue = js::ReturnValue; + +} // jsc + +namespace js { + +template<> +class String { + const JSStringRef m_str; + + public: + String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {} + String(const JSStringRef &s) : m_str(JSStringRetain(s)) {} + String(JSStringRef &&s) : m_str(s) {} + String(const std::string &str) : String(str.c_str()) {} + ~String() { JSStringRelease(m_str); } + + operator JSStringRef() const { + return m_str; + } + operator std::string() const { + size_t max_size = JSStringGetMaximumUTF8CStringSize(m_str); + std::string string; + string.resize(max_size); + string.resize(JSStringGetUTF8CString(m_str, &string[0], max_size) - 1); + return string; + } +}; + +template<> +class ReturnValue { + const JSContextRef m_context; + JSValueRef m_value = nullptr; + +public: + ReturnValue(JSContextRef ctx) : m_context(ctx) {} + + void set(const JSValueRef &value) { + m_value = value; + } + void set(const std::string &string) { + m_value = JSValueMakeString(m_context, jsc::String(string)); + } + void set(bool boolean) { + m_value = JSValueMakeBoolean(m_context, boolean); + } + void set(double number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set(int32_t number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set(uint32_t number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set_null() { + m_value = JSValueMakeNull(m_context); + } + void set_undefined() { + m_value = JSValueMakeUndefined(m_context); + } + operator JSValueRef() const { + return m_value; + } +}; + +template<> +class Protected : public jsc::Protected { + public: + Protected(JSGlobalContextRef ctx) : jsc::Protected(ctx) { + JSGlobalContextRetain(*this); + } + ~Protected() { + JSGlobalContextRelease(*this); + } +}; + +template<> +class Protected : public jsc::Protected { + const JSGlobalContextRef m_context; + + public: + Protected(JSContextRef ctx, JSValueRef value) : jsc::Protected(value), m_context(JSContextGetGlobalContext(ctx)) { + JSValueProtect(m_context, *this); + } + ~Protected() { + JSValueUnprotect(m_context, *this); + } +}; + +template<> +class Protected : public Protected { + public: + Protected(JSContextRef ctx, JSObjectRef object) : Protected(ctx, object) {} + + operator JSObjectRef() const { + return static_cast(*this); + } +}; + +static inline bool is_object_of_type(JSContextRef ctx, JSValueRef value, jsc::String type) { + JSObjectRef global_object = JSContextGetGlobalObject(ctx); + JSValueRef exception = nullptr; + JSValueRef constructor = JSObjectGetProperty(ctx, global_object, type, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + + bool result = JSValueIsInstanceOfConstructor(ctx, value, jsc::Value::validated_to_constructor(ctx, constructor), &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + + return result; +} + +template<> +inline JSGlobalContextRef jsc::Context::get_global_context(JSContextRef ctx) { + return JSContextGetGlobalContext(ctx); +} + +template<> +inline bool jsc::Value::is_array(JSContextRef ctx, const JSValueRef &value) { + // JSValueIsArray() is not available until iOS 9. + static const jsc::String type = "Array"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_array_buffer(JSContextRef ctx, const JSValueRef &value) { + static const jsc::String type = "ArrayBuffer"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_date(JSContextRef ctx, const JSValueRef &value) { + static const jsc::String type = "Date"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_boolean(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsBoolean(ctx, value); +} + +template<> +inline bool jsc::Value::is_constructor(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); +} + +template<> +inline bool jsc::Value::is_function(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value) && JSObjectIsFunction(ctx, (JSObjectRef)value); +} + +template<> +inline bool jsc::Value::is_null(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsNull(ctx, value); +} + +template<> +inline bool jsc::Value::is_number(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsNumber(ctx, value); +} + +template<> +inline bool jsc::Value::is_object(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value); +} + +template<> +inline bool jsc::Value::is_string(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsString(ctx, value); +} + +template<> +inline bool jsc::Value::is_undefined(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsUndefined(ctx, value); +} + +template<> +inline bool jsc::Value::is_valid(const JSValueRef &value) { + return value != nullptr; +} + +template<> +JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) { + return JSValueMakeBoolean(ctx, boolean); +} + +template<> +JSValueRef jsc::Value::from_null(JSContextRef ctx) { + return JSValueMakeNull(ctx); +} + +template<> +JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) { + return JSValueMakeNumber(ctx, number); +} + +template<> +JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) { + return JSValueMakeString(ctx, string); +} + +template<> +JSValueRef jsc::Value::from_undefined(JSContextRef ctx) { + return JSValueMakeUndefined(ctx); +} + +template<> +inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) { + return JSValueToBoolean(ctx, value); +} + +template<> +inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + double number = JSValueToNumber(ctx, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + if (isnan(number)) { + throw std::invalid_argument("Value not convertible to a number."); + } + return number; +} + +template<> +inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + jsc::String string = JSValueToStringCopy(ctx, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return string; +} + +template<> +inline JSObjectRef jsc::Value::to_object(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(ctx, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return object; +} + +template<> +inline JSObjectRef jsc::Value::to_array(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_constructor(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, uint32_t argc, const JSValueRef arguments[]) { + JSValueRef exception = nullptr; + JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return result; +} + +template<> +inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, uint32_t argc, const JSValueRef arguments[]) { + JSValueRef exception = nullptr; + JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return result; +} + +template<> +inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { + return JSObjectHasProperty(ctx, object, key); +} + +template<> +inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { + return JSObjectHasProperty(ctx, object, jsc::String(util::to_string(index))); +} + +template<> +inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { + JSValueRef exception = nullptr; + JSValueRef value = JSObjectGetProperty(ctx, object, key, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return value; +} + +template<> +inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { + JSValueRef exception = nullptr; + JSValueRef value = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return value; +} + +template<> +inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key, const JSValueRef &value, PropertyAttributes attributes) { + JSValueRef exception = nullptr; + JSObjectSetProperty(ctx, object, key, value, attributes << 1, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } +} + +template<> +inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index, const JSValueRef &value) { + JSValueRef exception = nullptr; + JSObjectSetPropertyAtIndex(ctx, object, index, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } +} + +template<> +inline std::vector jsc::Object::get_property_names(JSContextRef ctx, const JSObjectRef &object) { + JSPropertyNameArrayRef property_names = JSObjectCopyPropertyNames(ctx, object); + size_t property_count = JSPropertyNameArrayGetCount(property_names); + + std::vector names; + names.reserve(property_count); + + for (size_t i = 0; i < property_count; i++) { + names.push_back(JSPropertyNameArrayGetNameAtIndex(property_names, i)); + } + + JSPropertyNameArrayRelease(property_names); + return names; +} + +template<> +inline JSValueRef jsc::Object::get_prototype(JSContextRef ctx, const JSObjectRef &object) { + return JSObjectGetPrototype(ctx, object); +} + +template<> +inline void jsc::Object::set_prototype(JSContextRef ctx, const JSObjectRef &object, const JSValueRef &prototype) { + JSObjectSetPrototype(ctx, object, prototype); +} + +template<> +inline JSObjectRef jsc::Object::create_empty(JSContextRef ctx) { + return JSObjectMake(ctx, nullptr, nullptr); +} + +template<> +inline JSObjectRef jsc::Object::create_array(JSContextRef ctx, uint32_t length, const JSValueRef values[]) { + JSValueRef exception = nullptr; + JSObjectRef array = JSObjectMakeArray(ctx, length, values, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return array; +} + +template<> +inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) { + JSValueRef number = jsc::Value::from_number(ctx, time); + return JSObjectMakeDate(ctx, 1, &number, nullptr); +} + +template<> +template +inline JSObjectRef jsc::Object::create(JSContextRef ctx, U* internal) { + return jsc::ObjectWrap::create(ctx, internal); +} + +template<> +template +inline bool jsc::Object::is_instance(JSContextRef ctx, const JSObjectRef &object) { + return jsc::ObjectWrap::has_instance(ctx, object); +} + +template<> +template +inline U* jsc::Object::get_internal(const JSObjectRef &object) { + return *static_cast *>(JSObjectGetPrivate(object)); +} + +template<> +template +inline void jsc::Object::set_internal(const JSObjectRef &object, U* ptr) { + auto wrap = static_cast *>(JSObjectGetPrivate(object)); + *wrap = ptr; +} + +template<> +inline JSValueRef jsc::Exception::value(JSContextRef ctx, const std::string &message) { + JSValueRef value = jsc::Value::from_string(ctx, message); + return JSObjectMakeError(ctx, 1, &value, NULL); +} + +} // js +} // realm diff --git a/src/jsc/jsc_util.hpp b/src/jsc/jsc_util.hpp index ce9b370d..b1fa4a24 100644 --- a/src/jsc/jsc_util.hpp +++ b/src/jsc/jsc_util.hpp @@ -18,21 +18,73 @@ #pragma once -#include -#include -#include - #include #include #include - #include #include + +#include "jsc_types.hpp" + #include "property.hpp" #include "schema.hpp" -namespace realm { -namespace js { +#define WRAP_EXCEPTION(METHOD, EXCEPTION, ARGS...) \ +try { METHOD(ARGS); } \ +catch(std::exception &e) { RJSSetException(ctx, EXCEPTION, e); } + +#define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ + JSValueRef returnValue = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnValue); \ + return returnValue; \ +} + +#define WRAP_CONSTRUCTOR(CLASS_NAME, METHOD_NAME) \ +JSObjectRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ + JSObjectRef returnObject = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, constructor, argumentCount, arguments, returnObject); \ + return returnObject; \ +} + +#define WRAP_PROPERTY_GETTER(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ + JSValueRef returnValue = NULL; \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, property, returnValue); \ + return returnValue; \ +} + +#define WRAP_PROPERTY_SETTER(CLASS_NAME, METHOD_NAME) \ +bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ + WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, property, value); \ + return true; \ +} + +// for stol failure (std::invalid_argument) this could be another property that is handled externally, so ignore +#define WRAP_INDEXED_GETTER(CLASS_NAME, METHOD_NAME) \ +JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ + JSValueRef returnValue = NULL; \ + try { \ + size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ + CLASS_NAME::METHOD_NAME(ctx, object, index, returnValue); return returnValue; \ + } \ + catch (std::out_of_range &exp) { return JSValueMakeUndefined(ctx); } \ + catch (std::invalid_argument &exp) { return NULL; } \ + catch (std::exception &e) { RJSSetException(ctx, *ex, e); return NULL; } \ +} + +#define WRAP_INDEXED_SETTER(CLASS_NAME, METHOD_NAME) \ +bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ + try { \ + size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ + { CLASS_NAME::METHOD_NAME(ctx, object, index, value); return true; } \ + } \ + catch (std::out_of_range &exp) { RJSSetException(ctx, *ex, exp); } \ + catch (std::invalid_argument &exp) { *ex = RJSMakeError(ctx, "Invalid index"); } \ + catch (std::exception &e) { RJSSetException(ctx, *ex, e); } \ + return false; \ +} + template inline void RJSFinalize(JSObjectRef object) { @@ -40,15 +92,6 @@ inline void RJSFinalize(JSObjectRef object) { JSObjectSetPrivate(object, NULL); } -template -inline JSObjectRef RJSWrapObject(JSContextRef ctx, JSClassRef jsClass, T object, JSValueRef prototype = NULL) { - JSObjectRef ref = JSObjectMake(ctx, jsClass, (void *)object); - if (prototype) { - JSObjectSetPrototype(ctx, ref, prototype); - } - return ref; -} - template inline T RJSGetInternal(JSObjectRef jsObject) { return static_cast(JSObjectGetPrivate(jsObject)); @@ -56,7 +99,7 @@ inline T RJSGetInternal(JSObjectRef jsObject) { template JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL, - JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL) { + JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL, const JSStaticValue *values = NULL) { JSClassDefinition classDefinition = kJSClassDefinitionEmpty; classDefinition.className = name; classDefinition.finalize = RJSFinalize; @@ -65,6 +108,7 @@ JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback classDefinition.staticFunctions = funcs; classDefinition.getPropertyNames = propertyNames; classDefinition.parentClass = parentClass; + classDefinition.staticValues = values; return JSClassCreate(&classDefinition); } @@ -75,24 +119,6 @@ std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const JSStringRef RJSStringForString(const std::string &str); JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str); -inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected, const char *message = NULL) { - if (argumentCount != expected) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - -inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected, const char *message = NULL) { - if (argumentCount < expected) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - -inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max, const char *message = NULL) { - if (argumentCount < min || argumentCount > max) { - throw std::invalid_argument(message ?: "Invalid arguments"); - } -} - class RJSException : public std::runtime_error { public: RJSException(JSContextRef ctx, JSValueRef &ex) : std::runtime_error(RJSStringForValue(ctx, ex)), @@ -111,7 +137,7 @@ bool RJSIsValueArray(JSContextRef ctx, JSValueRef value); bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value); bool RJSIsValueDate(JSContextRef ctx, JSValueRef value); -static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object) { throw std::runtime_error(message ?: "Value is not an object."); @@ -119,7 +145,7 @@ static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef return object; } -static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object || !RJSIsValueDate(ctx, object)) { throw std::runtime_error(message ?: "Value is not a date."); @@ -127,7 +153,7 @@ static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef v return object; } -static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = NULL) { +static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { JSObjectRef object = JSValueToObject(ctx, value, NULL); if (!object || !JSObjectIsFunction(ctx, object)) { throw std::runtime_error(message ?: "Value is not a function."); @@ -151,6 +177,13 @@ static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef valu return number; } +static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value, const char *err = nullptr) { + if (!JSValueIsBoolean(ctx, value)) { + throw std::invalid_argument(err ?: "Value is not a boolean."); + } + return JSValueToBoolean(ctx, value); +} + static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { JSValueRef exception = NULL; JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); @@ -169,7 +202,7 @@ static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectR return propertyValue; } -static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) { +static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = nullptr) { JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); if (JSValueIsUndefined(ctx, propertyValue)) { throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined"); @@ -247,5 +280,24 @@ static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JS return ret; } - -}} + +static inline void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { + returnObject = JSValueMakeUndefined(ctx); +} + +template +static inline void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { + returnObject = JSValueMakeNumber(ctx, number); +} + +static inline void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { + returnObject = JSObjectMakeArray(ctx, count, objects, NULL); +} + +static inline void RJSSetException(JSContextRef ctx, JSValueRef &exceptionObject, std::exception &exception) { + exceptionObject = RJSMakeError(ctx, exception); +} + +static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) { + return JSValueIsObjectOfClass(ctx, value, jsClass); +} diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp new file mode 100644 index 00000000..b85141bb --- /dev/null +++ b/src/node/node_class.hpp @@ -0,0 +1,278 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" +#include "js_class.hpp" + +namespace realm { +namespace node { + +template +using ObjectClass = js::ObjectClass; + +using ConstructorType = js::ConstructorType; +using MethodType = js::MethodType; +using PropertyGetterType = js::PropertyGetterType; +using PropertySetterType = js::PropertySetterType; +using IndexPropertyGetterType = js::IndexPropertyGetterType; +using IndexPropertySetterType = js::IndexPropertySetterType; +using StringPropertyGetterType = js::StringPropertyGetterType; +using StringPropertySetterType = js::StringPropertySetterType; +using StringPropertyEnumeratorType = js::StringPropertyEnumeratorType; +using PropertyType = js::PropertyType; + +static inline std::vector> get_arguments(const Nan::FunctionCallbackInfo &info) { + int count = info.Length(); + std::vector> arguments; + arguments.reserve(count); + + for (int i = 0; i < count; i++) { + arguments.push_back(info[i]); + } + + return arguments; +} + +static inline void setup_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { + Nan::HandleScope scope; + + v8::Local signature = Nan::New(tpl); + v8::Local t = Nan::New(callback, v8::Local(), signature); + v8::Local fn = Nan::GetFunction(t).ToLocalChecked(); + v8::Local fn_name = Nan::New(name).ToLocalChecked(); + + // The reason we use this rather than Nan::SetPrototypeMethod is DontEnum. + tpl->PrototypeTemplate()->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); + fn->SetName(fn_name); +} + +static inline void setup_property(v8::Local tpl, const std::string &name, const PropertyType &property) { + Nan::HandleScope scope; + + v8::Local prop_name = Nan::New(name).ToLocalChecked(); + v8::PropertyAttribute attributes = static_cast(v8::DontEnum | v8::DontDelete | (property.setter ? v8::None : v8::ReadOnly)); + + Nan::SetAccessor(tpl, prop_name, property.getter, property.setter, v8::Local(), v8::DEFAULT, attributes); +} + +template +class ObjectWrap : public Nan::ObjectWrap { + static ObjectClass s_class; + static Nan::Persistent s_constructor; + static Nan::Persistent s_template; + + std::unique_ptr m_object; + + ObjectWrap(T* object = nullptr) : m_object(object) {} + + public: + operator T*() const { + return m_object.get(); + } + ObjectWrap& operator=(T* object) { + if (m_object.get() != object) { + m_object = std::unique_ptr(object); + } + return *this; + } + + static v8::Local create(v8::Isolate* isolate, T* internal = nullptr) { + Nan::EscapableHandleScope scope; + + // TODO: Figure out why this template ends up being empty here. + v8::Local tpl = Nan::New(s_template); + v8::Local instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked(); + + auto wrap = new ObjectWrap(internal); + wrap->Wrap(instance); + + return scope.Escape(instance); + } + + static bool has_instance(v8::Isolate* isolate, const v8::Local &value) { + return Nan::New(s_template)->HasInstance(value); + } + + static NAN_MODULE_INIT(init) { + v8::Local tpl = Nan::New(construct); + v8::Local instance_tpl = tpl->InstanceTemplate(); + v8::Local name = Nan::New(s_class.name).ToLocalChecked(); + + tpl->SetClassName(name); + instance_tpl->SetInternalFieldCount(1); + + // TODO: Setup static properties and methods. + for (auto &pair : s_class.methods) { + setup_method(tpl, pair.first, pair.second); + } + for (auto &pair : s_class.properties) { + setup_property(instance_tpl, pair.first, pair.second); + } + + if (s_class.index_accessor.getter) { + // TODO: Add our own index enumerator. + auto &index_accessor = s_class.index_accessor; + Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter); + } + if (s_class.string_accessor.getter) { + auto &string_accessor = s_class.string_accessor; + Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter, string_accessor.setter, 0, 0, string_accessor.enumerator); + } + + v8::Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); + s_constructor.Reset(constructor); + s_template.Reset(tpl); + + Nan::Set(target, name, constructor); + } + + static NAN_METHOD(construct) { + if (!info.IsConstructCall()) { + Nan::ThrowError("Constructor must be called with new"); + } + if (s_class.constructor) { + auto isolate = info.GetIsolate(); + auto arguments = get_arguments(info); + v8::Local this_object = info.This(); + info.GetReturnValue().Set(this_object); + + auto wrap = new ObjectWrap(); + wrap->Wrap(this_object); + + try { + s_class.constructor(isolate, this_object, arguments.size(), arguments.data()); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } + } + else { + Nan::ThrowError("Illegal constructor"); + } + } +}; + +// The declared static variables must be defined as well. +template ObjectClass ObjectWrap::s_class; +template Nan::Persistent ObjectWrap::s_constructor; +template Nan::Persistent ObjectWrap::s_template; + +} // node + +namespace js { + +template +void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + node::ReturnValue return_value(info.GetReturnValue()); + auto arguments = node::get_arguments(info); + + try { + F(isolate, info.This(), arguments.size(), arguments.data(), return_value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(v8::Local property, Nan::NAN_GETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + node::ReturnValue return_value(info.GetReturnValue()); + try { + F(isolate, info.This(), return_value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(v8::Local property, v8::Local value, Nan::NAN_SETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + try { + F(isolate, info.This(), value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + node::ReturnValue return_value(info.GetReturnValue()); + try { + F(isolate, info.This(), index, return_value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + try { + F(isolate, info.This(), index, value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + node::ReturnValue return_value(info.GetReturnValue()); + try { + F(isolate, info.This(), property, return_value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { + v8::Isolate* isolate = info.GetIsolate(); + try { + F(isolate, info.This(), property, value); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + +template +void wrap(Nan::NAN_PROPERTY_ENUMERATOR_ARGS_TYPE info) { + auto names = F(info.GetIsolate(), info.This()); + int count = (int)names.size(); + v8::Local array = Nan::New(count); + + for (int i = 0; i < count; i++) { + Nan::Set(array, i, v8::Local(names[i])); + } + + info.GetReturnValue().Set(array); +} + +} // js +} // realm diff --git a/src/node/node_dummy.c b/src/node/node_dummy.c new file mode 100644 index 00000000..1221fe3b --- /dev/null +++ b/src/node/node_dummy.c @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +void node_module_register(void* mod) {} diff --git a/src/node/node_init.cpp b/src/node/node_init.cpp new file mode 100644 index 00000000..4ba03030 --- /dev/null +++ b/src/node/node_init.cpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "node_init.hpp" +#include "js_realm.hpp" +#include "js_list.hpp" + +namespace realm { +namespace node { + +static void init(v8::Local exports) { + ObjectWrap::init(exports); + ObjectWrap::init(exports); +// RealmObjectWrap::Init(exports); +// RealmResultsWrap::Init(exports); +// RealmListWrap::Init(exports); +// +// NODE_SET_METHOD(exports, "__clearCaches", ClearCaches); +} + +} // node +} // realm + +NODE_MODULE(Realm, realm::node::init); diff --git a/src/node/node_init.hpp b/src/node/node_init.hpp new file mode 100644 index 00000000..ed47542c --- /dev/null +++ b/src/node/node_init.hpp @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_object_accessor.hpp" diff --git a/src/node/node_object_accessor.cpp b/src/node/node_object_accessor.cpp new file mode 100644 index 00000000..3c244331 --- /dev/null +++ b/src/node/node_object_accessor.cpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "node_object_accessor.hpp" + +using namespace realm; +using namespace realm::node; + +using Accessor = js::NativeAccessor; + +template<> +std::string Accessor::to_binary(v8::Isolate* isolate, v8::Local &value) { + // TODO + return std::string(); +} + +template<> +v8::Local Accessor::from_binary(v8::Isolate* isolate, BinaryData data) { + // TODO + return v8::Local(); +} diff --git a/src/node/node_object_accessor.hpp b/src/node/node_object_accessor.hpp new file mode 100644 index 00000000..975aab37 --- /dev/null +++ b/src/node/node_object_accessor.hpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_class.hpp" +#include "js_object_accessor.hpp" + +namespace realm { + +// Specialize a native accessor class for Node. +template<> +class NativeAccessor : public js::NativeAccessor {}; + +} // realm diff --git a/src/node/node_types.hpp b/src/node/node_types.hpp new file mode 100644 index 00000000..563bc17a --- /dev/null +++ b/src/node/node_types.hpp @@ -0,0 +1,473 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include + +#include + +#include "js_types.hpp" + +namespace realm { +namespace node { + +struct Types { + using Context = v8::Isolate*; + using GlobalContext = v8::Local; + using Value = v8::Local; + using Object = v8::Local; + using String = v8::Local; + using Function = v8::Local; + + using ConstructorCallback = Nan::FunctionCallback; + using FunctionCallback = Nan::FunctionCallback; + using PropertyGetterCallback = Nan::GetterCallback; + using PropertySetterCallback = Nan::SetterCallback; + using IndexPropertyGetterCallback = Nan::IndexGetterCallback; + using IndexPropertySetterCallback = Nan::IndexSetterCallback; + using StringPropertyGetterCallback = Nan::PropertyGetterCallback; + using StringPropertySetterCallback = Nan::PropertySetterCallback; + using StringPropertyEnumeratorCallback = Nan::PropertyEnumeratorCallback; +}; + +template +class Protected { + // TODO: Figure out why Nan::CopyablePersistentTraits causes a build failure. + Nan::Persistent> m_value; + + public: + Protected(v8::Local value) : m_value(value) {} + + operator v8::Local() const { + return Nan::New(m_value); + } + bool operator==(const v8::Local &other) const { + return m_value == other; + } + bool operator!=(const v8::Local &other) const { + return m_value != other; + } + bool operator==(const Protected &other) const { + return m_value == other.m_value; + } + bool operator!=(const Protected &other) const { + return m_value != other.m_value; + } + bool operator<(const Protected &other) const { + return *Nan::New(m_value) < *Nan::New(other.m_value); + } +}; + +template +class ObjectWrap; + +using String = js::String; +using Context = js::Context; +using Value = js::Value; +using Function = js::Function; +using Object = js::Object; +using Exception = js::Exception; +using ReturnValue = js::ReturnValue; + +} // node + +namespace js { + +template<> +class String { + std::string m_str; + + public: + String(const char* s) : m_str(s) {} + String(const std::string &s) : m_str(s) {} + String(const v8::Local &s) : m_str(*Nan::Utf8String(s)) {} + String(v8::Local &&s) : String(s) {} + + operator std::string() const { + return m_str; + } + operator v8::Local() const { + return Nan::New(m_str).ToLocalChecked(); + } +}; + +template<> +class ReturnValue { + Nan::ReturnValue m_value; + + public: + ReturnValue(Nan::ReturnValue value) : m_value(value) {} + + void set(const v8::Local &value) { + m_value.Set(value); + } + void set(const std::string &string) { + if (string.empty()) { + m_value.SetEmptyString(); + } + else { + m_value.Set(Nan::New(string).ToLocalChecked()); + } + } + void set(bool boolean) { + m_value.Set(boolean); + } + void set(double number) { + m_value.Set(number); + } + void set(int32_t number) { + m_value.Set(number); + } + void set(uint32_t number) { + m_value.Set(number); + } + void set_null() { + m_value.SetNull(); + } + void set_undefined() { + m_value.SetUndefined(); + } +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Local ctx) : node::Protected(ctx) {} + + operator v8::Isolate*() const { + return v8::Local(*this)->GetIsolate(); + } +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local value) : node::Protected(value) {} +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} +}; + +template<> +inline v8::Local node::Context::get_global_context(v8::Isolate* isolate) { + return isolate->GetCurrentContext(); +} + +template<> +inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local &value) { + return value->IsArray(); +} + +template<> +inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local &value) { + return value->IsArrayBuffer(); +} + +template<> +inline bool node::Value::is_date(v8::Isolate* isolate, const v8::Local &value) { + return value->IsDate(); +} + +template<> +inline bool node::Value::is_boolean(v8::Isolate* isolate, const v8::Local &value) { + return value->IsBoolean(); +} + +template<> +inline bool node::Value::is_constructor(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction(); +} + +template<> +inline bool node::Value::is_function(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction(); +} + +template<> +inline bool node::Value::is_null(v8::Isolate* isolate, const v8::Local &value) { + return value->IsNull(); +} + +template<> +inline bool node::Value::is_number(v8::Isolate* isolate, const v8::Local &value) { + return value->IsNumber(); +} + +template<> +inline bool node::Value::is_object(v8::Isolate* isolate, const v8::Local &value) { + return value->IsObject(); +} + +template<> +inline bool node::Value::is_string(v8::Isolate* isolate, const v8::Local &value) { + return value->IsString(); +} + +template<> +inline bool node::Value::is_undefined(v8::Isolate* isolate, const v8::Local &value) { + return value->IsUndefined(); +} + +template<> +inline bool node::Value::is_valid(const v8::Local &value) { + return !value.IsEmpty(); +} + +template<> +inline v8::Local node::Value::from_boolean(v8::Isolate* isolate, bool boolean) { + return Nan::New(boolean); +} + +template<> +inline v8::Local node::Value::from_null(v8::Isolate* isolate) { + return Nan::Null(); +} + +template<> +inline v8::Local node::Value::from_number(v8::Isolate* isolate, double number) { + return Nan::New(number); +} + +template<> +inline v8::Local node::Value::from_string(v8::Isolate* isolate, const node::String &string) { + return v8::Local(string); +} + +template<> +inline v8::Local node::Value::from_undefined(v8::Isolate* isolate) { + return Nan::Undefined(); +} + +template<> +inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local &value) { + return Nan::To(value).FromMaybe(false); +} + +template<> +inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local &value) { + double number = Nan::To(value).FromMaybe(NAN); + if (isnan(number)) { + throw std::invalid_argument("Value not convertible to a number."); + } + return number; +} + +template<> +inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local &value) { + return value->ToString(); +} + +template<> +inline v8::Local node::Value::to_object(v8::Isolate* isolate, const v8::Local &value) { + return Nan::To(value).FromMaybe(v8::Local()); +} + +template<> +inline v8::Local node::Value::to_array(v8::Isolate* isolate, const v8::Local &value) { + return to_object(isolate, value); +} + +template<> +inline v8::Local node::Value::to_date(v8::Isolate* isolate, const v8::Local &value) { + return to_object(isolate, value); +} + +template<> +inline v8::Local node::Value::to_function(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction() ? v8::Local::Cast(value) : v8::Local(); +} + +template<> +inline v8::Local node::Value::to_constructor(v8::Isolate* isolate, const v8::Local &value) { + return to_function(isolate, value); +} + +template<> +inline v8::Local node::Function::call(v8::Isolate* isolate, const v8::Local &function, const v8::Local &this_object, uint32_t argc, const v8::Local arguments[]) { + Nan::TryCatch trycatch; + auto result = Nan::Call(function, this_object, argc, const_cast*>(arguments)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return result.ToLocalChecked(); +} + +template<> +inline v8::Local node::Function::construct(v8::Isolate* isolate, const v8::Local &function, uint32_t argc, const v8::Local arguments[]) { + Nan::TryCatch trycatch; + auto result = Nan::NewInstance(function, argc, const_cast*>(arguments)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return result.ToLocalChecked(); +} + +template<> +inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { + return Nan::Has(object, key).FromMaybe(false); +} + +template<> +inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { + return Nan::Has(object, index).FromMaybe(false); +} + +template<> +inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { + Nan::TryCatch trycatch; + auto value = Nan::Get(object, v8::Local(key)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return value.ToLocalChecked(); +} + +template<> +inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { + Nan::TryCatch trycatch; + auto value = Nan::Get(object, index); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return value.ToLocalChecked(); +} + +template<> +inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key, const v8::Local &value, PropertyAttributes attributes) { + Nan::TryCatch trycatch; + + if (attributes) { + Nan::ForceSet(object, v8::Local(key), value, v8::PropertyAttribute(attributes)); + } + else { + Nan::Set(object, v8::Local(key), value); + } + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } +} + +template<> +inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index, const v8::Local &value) { + Nan::TryCatch trycatch; + Nan::Set(object, index, value); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } +} + +template<> +inline std::vector node::Object::get_property_names(v8::Isolate* isolate, const v8::Local &object) { + auto maybe_array = Nan::GetPropertyNames(object); + if (maybe_array.IsEmpty()) { + return std::vector(); + } + + auto array = maybe_array.ToLocalChecked(); + uint32_t count = array->Length(); + + std::vector names; + names.reserve(count); + + for (uint32_t i = 0; i < count; i++) { + names.push_back(array->Get(i)->ToString()); + } + + return names; +} + +template<> +inline v8::Local node::Object::get_prototype(v8::Isolate* isolate, const v8::Local &object) { + return object->GetPrototype(); +} + +template<> +inline void node::Object::set_prototype(v8::Isolate* isolate, const v8::Local &object, const v8::Local &prototype) { + Nan::SetPrototype(object, prototype); +} + +template<> +inline v8::Local node::Object::create_empty(v8::Isolate* isolate) { + return Nan::New(); +} + +template<> +inline v8::Local node::Object::create_array(v8::Isolate* isolate, uint32_t length, const v8::Local values[]) { + v8::Local array = Nan::New(length); + for (uint32_t i = 0; i < length; i++) { + set_property(isolate, array, i, values[i]); + } + return array; +} + +template<> +inline v8::Local node::Object::create_date(v8::Isolate* isolate, double time) { + return Nan::New(time).ToLocalChecked(); +} + +template<> +template +inline v8::Local node::Object::create(v8::Isolate* isolate, U* internal) { + return node::ObjectWrap::create(isolate, internal); +} + +template<> +template +inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local &object) { + return node::ObjectWrap::has_instance(isolate, object); +} + +template<> +template +inline U* node::Object::get_internal(const v8::Local &object) { + return *Nan::ObjectWrap::Unwrap>(object); +} + +template<> +template +inline void node::Object::set_internal(const v8::Local &object, U* ptr) { + auto wrap = Nan::ObjectWrap::Unwrap>(object); + *wrap = ptr; +} + +template<> +inline v8::Local node::Exception::value(v8::Isolate* isolate, const std::string &message) { + return Nan::Error(message.c_str()); +} + +} // js +} // realm diff --git a/src/rpc.cpp b/src/rpc.cpp index e615eacf..89494d07 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -21,7 +21,7 @@ #include #include #include -#include "js_init.h" +#include "jsc_init.h" #include "js_object.hpp" #include "js_results.hpp" #include "jsc_list.hpp" From c0c65ad620f9a214bc90d428859a854b1b96f4fc Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 12 Apr 2016 14:42:05 -0700 Subject: [PATCH 15/48] Make all JSC tests pass again --- react-native/ios/RealmReact/RealmReact.mm | 10 +- src/ios/RealmJS.xcodeproj/project.pbxproj | 45 +-- src/js_class.hpp | 27 +- src/{jsc/jsc_list.hpp => js_collection.hpp} | 18 +- src/js_list.hpp | 26 +- src/js_object.hpp | 17 +- src/js_object_accessor.hpp | 2 +- src/js_realm.cpp | 17 +- src/js_realm.hpp | 62 ++-- src/js_results.cpp | 256 ----------------- src/js_results.hpp | 230 ++++++++++++++- src/js_schema.hpp | 2 +- src/js_types.hpp | 18 +- src/jsc/jsc_class.hpp | 222 +++++++++----- src/jsc/jsc_collection.cpp | 30 -- src/jsc/jsc_collection.hpp | 23 -- src/jsc/jsc_init.cpp | 59 +--- src/jsc/jsc_init.hpp | 1 + src/jsc/jsc_list.cpp | 82 ------ src/jsc/jsc_realm.cpp | 111 ------- src/jsc/jsc_realm.hpp | 24 -- src/jsc/jsc_types.hpp | 43 ++- src/jsc/jsc_util.hpp | 303 -------------------- src/node/node_class.hpp | 3 + src/node/node_init.cpp | 2 - src/node/node_init.hpp | 1 + src/node/node_types.hpp | 8 +- src/rpc.cpp | 184 +++++------- src/rpc.hpp | 14 +- 29 files changed, 637 insertions(+), 1203 deletions(-) rename src/{jsc/jsc_list.hpp => js_collection.hpp} (72%) delete mode 100644 src/js_results.cpp delete mode 100644 src/jsc/jsc_collection.cpp delete mode 100644 src/jsc/jsc_collection.hpp delete mode 100644 src/jsc/jsc_list.cpp delete mode 100644 src/jsc/jsc_realm.cpp delete mode 100644 src/jsc/jsc_realm.hpp delete mode 100644 src/jsc/jsc_util.hpp diff --git a/react-native/ios/RealmReact/RealmReact.mm b/react-native/ios/RealmReact/RealmReact.mm index e07c5208..3299201c 100644 --- a/react-native/ios/RealmReact/RealmReact.mm +++ b/react-native/ios/RealmReact/RealmReact.mm @@ -20,7 +20,7 @@ #import "RealmAnalytics.h" #import "RCTBridge.h" -#import "js_init.h" +#import "jsc_init.h" #import "shared_realm.hpp" #import "realm_coordinator.hpp" @@ -39,6 +39,8 @@ #define WEB_SERVER_PORT 8082 #endif +using namespace realm::rpc; + @interface NSObject () - (instancetype)initWithJSContext:(void *)context; - (JSGlobalContextRef)ctx; @@ -82,7 +84,7 @@ extern "C" JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executo #if DEBUG GCDWebServer *_webServer; - std::unique_ptr _rpcServer; + std::unique_ptr _rpcServer; #endif } @@ -204,7 +206,7 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) { - (void)startRPC { [GCDWebServer setLogLevel:3]; _webServer = [[GCDWebServer alloc] init]; - _rpcServer = std::make_unique(); + _rpcServer = std::make_unique(); __weak __typeof__(self) weakSelf = self; // Add a handler to respond to POST requests on any URL @@ -219,7 +221,7 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) { RealmReact *self = weakSelf; if (self) { if (_rpcServer) { - realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]); + json args = json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]); std::string responseText = _rpcServer->perform_request(request.path.UTF8String, args).dump(); responseData = [NSData dataWithBytes:responseText.c_str() length:responseText.length()]; return; diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 7602ddf3..536a75fd 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; }; - 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 025678961CAB478800FB8501 /* jsc_realm.cpp */; }; 0270BC821B7D020100010E03 /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */; }; 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 029048121C0428DF00ABDED4 /* jsc_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* jsc_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -17,11 +16,9 @@ 029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048081C0428DF00ABDED4 /* js_realm.hpp */; }; 0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480A1C0428DF00ABDED4 /* js_results.hpp */; }; 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480C1C0428DF00ABDED4 /* js_schema.hpp */; }; - 0290481E1C0428DF00ABDED4 /* jsc_util.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */; }; 029048201C0428DF00ABDED4 /* rpc.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048101C0428DF00ABDED4 /* rpc.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; 029048371C042A3C00ABDED4 /* platform.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048351C042A3C00ABDED4 /* platform.hpp */; }; 0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0290483A1C042EE200ABDED4 /* RealmJS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */; }; 02B58CCE1AE99D4D009B348C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */; }; 02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; }; @@ -63,7 +60,6 @@ F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; }; F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; }; F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; }; - F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048091C0428DF00ABDED4 /* js_results.cpp */; }; F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480B1C0428DF00ABDED4 /* js_schema.cpp */; }; F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; }; F63FF2DE1C1565EA00B3B8E0 /* RealmJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */; }; @@ -86,8 +82,6 @@ F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; }; F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6BB7DF01BF681BC00D0A69E /* base64.hpp */; }; F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; }; - F6CB31001C8EDDAB0070EF3F /* jsc_collection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */; }; - F6CB31011C8EDDBB0070EF3F /* jsc_collection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -122,8 +116,6 @@ /* Begin PBXFileReference section */ 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = ""; }; 025678951CAB392000FB8501 /* jsc_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_types.hpp; sourceTree = ""; }; - 025678961CAB478800FB8501 /* jsc_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_realm.cpp; sourceTree = ""; }; - 025678971CAB478800FB8501 /* jsc_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_realm.hpp; sourceTree = ""; }; 0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = ""; }; 0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = ""; }; @@ -134,19 +126,15 @@ 029048061C0428DF00ABDED4 /* js_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_object.hpp; sourceTree = ""; }; 029048071C0428DF00ABDED4 /* js_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_realm.cpp; sourceTree = ""; }; 029048081C0428DF00ABDED4 /* js_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm.hpp; sourceTree = ""; }; - 029048091C0428DF00ABDED4 /* js_results.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_results.cpp; sourceTree = ""; }; 0290480A1C0428DF00ABDED4 /* js_results.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_results.hpp; sourceTree = ""; }; 0290480B1C0428DF00ABDED4 /* js_schema.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_schema.cpp; sourceTree = ""; }; 0290480C1C0428DF00ABDED4 /* js_schema.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_schema.hpp; sourceTree = ""; }; - 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_util.hpp; sourceTree = ""; }; 0290480F1C0428DF00ABDED4 /* rpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rpc.cpp; sourceTree = ""; }; 029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = ""; }; 029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = ""; }; 029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = ""; }; 0290483A1C042EE200ABDED4 /* RealmJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmJS.h; sourceTree = ""; }; 02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; - 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_list.cpp; sourceTree = ""; }; - 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_list.hpp; sourceTree = ""; }; 02B58CB11AE99CEC009B348C /* RealmJS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmJS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -190,6 +178,7 @@ F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = ""; }; F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = ""; }; F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = ""; }; + F60102F71CBDA6D400EC01BA /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = ""; }; F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; F620F0521CAF0B600082977B /* js_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_class.hpp; sourceTree = ""; }; F620F0531CAF2EF70082977B /* jsc_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_class.hpp; sourceTree = ""; }; @@ -244,8 +233,6 @@ F6BB7DF01BF681BC00D0A69E /* base64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = ""; }; F6BCCFDF1C83809A00FE31AE /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = ../../lib; sourceTree = SOURCE_ROOT; }; F6C3FBBC1BF680EC00E6FFD4 /* json.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = ""; }; - F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_collection.cpp; sourceTree = ""; }; - F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_collection.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -293,12 +280,12 @@ F6874A441CAD2ACD00EEEE36 /* JSC */, F62BF9001CAC72C40022BCDC /* Node */, F62A35141C18E783004A917D /* Object Store */, + F60102F71CBDA6D400EC01BA /* js_collection.hpp */, 029048041C0428DF00ABDED4 /* js_list.hpp */, F620F0591CB7B4C80082977B /* js_object_accessor.hpp */, 029048061C0428DF00ABDED4 /* js_object.hpp */, 029048071C0428DF00ABDED4 /* js_realm.cpp */, 029048081C0428DF00ABDED4 /* js_realm.hpp */, - 029048091C0428DF00ABDED4 /* js_results.cpp */, 0290480A1C0428DF00ABDED4 /* js_results.hpp */, 0290480B1C0428DF00ABDED4 /* js_schema.cpp */, 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, @@ -503,13 +490,6 @@ 029048011C0428DF00ABDED4 /* jsc_init.cpp */, F620F0531CAF2EF70082977B /* jsc_class.hpp */, 025678951CAB392000FB8501 /* jsc_types.hpp */, - 0290480E1C0428DF00ABDED4 /* jsc_util.hpp */, - F6CB30FD1C8EDD760070EF3F /* jsc_collection.hpp */, - F6CB30FC1C8EDD760070EF3F /* jsc_collection.cpp */, - 02AFE5881CA9B23400953DA3 /* jsc_list.hpp */, - 02AFE5871CA9B23400953DA3 /* jsc_list.cpp */, - 025678971CAB478800FB8501 /* jsc_realm.hpp */, - 025678961CAB478800FB8501 /* jsc_realm.cpp */, F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */, F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */, ); @@ -539,12 +519,10 @@ 0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */, 029048121C0428DF00ABDED4 /* jsc_init.h in Headers */, 029048201C0428DF00ABDED4 /* rpc.hpp in Headers */, - 0290481E1C0428DF00ABDED4 /* jsc_util.hpp in Headers */, 0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */, 029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */, 029048141C0428DF00ABDED4 /* js_list.hpp in Headers */, F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */, - F6CB31011C8EDDBB0070EF3F /* jsc_collection.hpp in Headers */, 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */, 029048161C0428DF00ABDED4 /* js_object.hpp in Headers */, 029048371C042A3C00ABDED4 /* platform.hpp in Headers */, @@ -788,12 +766,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F6CB31001C8EDDAB0070EF3F /* jsc_collection.cpp in Sources */, 02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */, - 025678981CAB478800FB8501 /* jsc_realm.cpp in Sources */, F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */, 02F59EC31C88F17D007F774C /* results.cpp in Sources */, - 02AFE5891CA9B23400953DA3 /* jsc_list.cpp in Sources */, F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */, F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */, 02F59ECA1C88F190007F774C /* parser.cpp in Sources */, @@ -802,7 +777,6 @@ 02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */, 02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */, F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */, - F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */, 02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */, F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */, 02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */, @@ -912,6 +886,7 @@ "$(OTHER_CFLAGS)", "-isystem", ../../core/include, + "-ftemplate-backtrace-limit=0", ); SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -968,6 +943,7 @@ "$(OTHER_CFLAGS)", "-isystem", ../../core/include, + "-ftemplate-backtrace-limit=0", ); SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1109,6 +1085,7 @@ "$(OTHER_CFLAGS)", "-isystem", ../../core/include, + "-ftemplate-backtrace-limit=0", ); SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1179,10 +1156,6 @@ "$(inherited)", /usr/local/lib, ); - OTHER_CPLUSPLUSFLAGS = ( - "$(inherited)", - "-ftemplate-backtrace-limit=0", - ); OTHER_LDFLAGS = ( "-lrealm", "-lv8", @@ -1207,10 +1180,6 @@ "$(inherited)", /usr/local/lib, ); - OTHER_CPLUSPLUSFLAGS = ( - "$(inherited)", - "-ftemplate-backtrace-limit=0", - ); OTHER_LDFLAGS = ( "-lrealm", "-lv8", @@ -1235,10 +1204,6 @@ "$(inherited)", /usr/local/lib, ); - OTHER_CPLUSPLUSFLAGS = ( - "$(inherited)", - "-ftemplate-backtrace-limit=0", - ); OTHER_LDFLAGS = ( "-lrealm", "-lv8", diff --git a/src/js_class.hpp b/src/js_class.hpp index 6168eedb..046f23db 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -79,22 +79,29 @@ using MethodMap = std::map; template using PropertyMap = std::map>; -template -struct BaseObjectClass { +template +struct ObjectClass { + // Every specialization *must* at least have a name. std::string name; - BaseObjectClass* superclass; +}; +template +struct BaseObjectClass { + // This pointer does not need to be set. + ObjectClass* superclass; + + // ObjectClass specializations should inherit from this class and override what's needed below. ConstructorType* constructor; - MethodMap static_methods = MethodMap(); - PropertyMap static_properties = PropertyMap(); - MethodMap methods = MethodMap(); - PropertyMap properties = PropertyMap(); - IndexPropertyType index_accessor = {}; - StringPropertyType string_accessor = {}; + MethodMap static_methods; + PropertyMap static_properties; + MethodMap methods; + PropertyMap properties; + IndexPropertyType index_accessor; + StringPropertyType string_accessor; }; template -struct ObjectClass; +class ObjectWrap; } // js } // realm diff --git a/src/jsc/jsc_list.hpp b/src/js_collection.hpp similarity index 72% rename from src/jsc/jsc_list.hpp rename to src/js_collection.hpp index df11a017..1a1c5f02 100644 --- a/src/jsc/jsc_list.hpp +++ b/src/js_collection.hpp @@ -18,8 +18,18 @@ #pragma once -#include "jsc_types.hpp" -#include "list.hpp" +#include "js_class.hpp" -JSClassRef RJSListClass(); -JSObjectRef RJSListCreate(JSContextRef ctx, realm::List &list); +namespace realm { +namespace js { + +// Empty class that merely serves as useful type for now. +class Collection {}; + +template +struct ObjectClass : BaseObjectClass { + std::string const name = "Collection"; +}; + +} // js +} // realm diff --git a/src/js_list.hpp b/src/js_list.hpp index 87f2113a..7c2e82a6 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -18,11 +18,9 @@ #pragma once -#include - -#include "js_class.hpp" +#include "js_collection.hpp" #include "js_object.hpp" -// TODO: #include "js_results.hpp" +#include "js_results.hpp" #include "js_types.hpp" #include "js_util.hpp" @@ -60,7 +58,7 @@ struct List { }; template -struct ObjectClass : BaseObjectClass { +struct ObjectClass : BaseObjectClass { using List = List; std::string const name = "List"; @@ -175,7 +173,7 @@ void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const auto list = get_internal(this_object); size_t size = list->size(); - long index = std::min(Value::validated_to_number(ctx, arguments[0]), size); + long index = std::min(Value::to_number(ctx, arguments[0]), size); if (index < 0) { index = std::max(size + index, 0); } @@ -185,7 +183,7 @@ void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const remove = size - index; } else { - remove = std::max(Value::validated_to_number(ctx, arguments[1]), 0); + remove = std::max(Value::to_number(ctx, arguments[1]), 0); remove = std::min(remove, size - index); } @@ -210,9 +208,7 @@ void List::StaticResults(ContextType ctx, ObjectType this_object, size_t argc validate_argument_count(argc, 0); auto list = get_internal(this_object); - - // TODO: Results - // return_value = RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false); + return_value.set(Results::create(ctx, *list, false)); } template @@ -220,10 +216,7 @@ void List::Filtered(ContextType ctx, ObjectType this_object, size_t argc, con validate_argument_count_at_least(argc, 1); auto list = get_internal(this_object); - auto sharedRealm = *get_internal(this_object); - - // TODO: Results - // return_value = RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argc, arguments); + return_value.set(Results::create_filtered(ctx, *list, argc, arguments)); } template @@ -231,10 +224,7 @@ void List::Sorted(ContextType ctx, ObjectType this_object, size_t argc, const validate_argument_count(argc, 1, 2); auto list = get_internal(this_object); - auto sharedRealm = *get_internal(this_object); - - // TODO: Results - // return_value = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argc, arguments); + return_value.set(Results::create_sorted(ctx, *list, argc, arguments)); } } // js diff --git a/src/js_object.hpp b/src/js_object.hpp index a6782b2d..60e9e5d7 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -43,7 +43,7 @@ struct RealmObject { static ObjectType create(ContextType, realm::Object &); static void GetProperty(ContextType, ObjectType, const String &, ReturnValue &); - static bool SetProperty(ContextType, ObjectType, const String &, ValueType, ReturnValue &); + static bool SetProperty(ContextType, ObjectType, const String &, ValueType); static std::vector GetPropertyNames(ContextType, ObjectType); }; @@ -87,7 +87,7 @@ typename T::Object RealmObject::create(ContextType ctx, realm::Object &realm_ template void RealmObject::GetProperty(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { try { - auto realm_object = get_internal(object); + auto realm_object = get_internal(object); auto result = realm_object->template get_property_value(ctx, property); return_value.set(result); } catch (InvalidPropertyException &ex) { @@ -96,21 +96,22 @@ void RealmObject::GetProperty(ContextType ctx, ObjectType object, const Strin } template -bool RealmObject::SetProperty(ContextType ctx, ObjectType object, const String &property, ValueType value, ReturnValue &return_value) { - auto realm_object = get_internal(object); +bool RealmObject::SetProperty(ContextType ctx, ObjectType object, const String &property, ValueType value) { + auto realm_object = get_internal(object); realm_object->set_property_value(ctx, property, value, true); return true; } template std::vector> RealmObject::GetPropertyNames(ContextType ctx, ObjectType object) { - auto realm_object = get_internal(object); + auto realm_object = get_internal(object); auto &properties = realm_object->get_object_schema().properties; - std::vector names(properties.size()); - size_t index = 0; + + std::vector names; + names.reserve(properties.size()); for (auto &prop : properties) { - names[index++] = prop.name; + names.push_back(prop.name); } return names; diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index c3d27a5d..f9788acd 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -84,7 +84,7 @@ class NativeAccessor { return Value::validated_to_string(ctx, value, "Property"); } static ValueType from_string(ContextType ctx, StringData string) { - return Value::from_string(ctx, string); + return Value::from_string(ctx, string.data()); } static DateTime to_datetime(ContextType ctx, ValueType &value) { ObjectType date = Value::validated_to_date(ctx, value, "Property"); diff --git a/src/js_realm.cpp b/src/js_realm.cpp index e7aedd2a..12932f93 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -17,20 +17,27 @@ //////////////////////////////////////////////////////////////////////////// #include "platform.hpp" +#include "realm_coordinator.hpp" namespace realm { namespace js { -static std::string s_defaultPath = ""; +static std::string s_default_path = ""; + std::string default_path() { - if (s_defaultPath.size() == 0) { - s_defaultPath = realm::default_realm_file_directory() + "/default.realm"; + if (s_default_path.size() == 0) { + s_default_path = realm::default_realm_file_directory() + "/default.realm"; } - return s_defaultPath; + return s_default_path; } void set_default_path(std::string path) { - s_defaultPath = path; + s_default_path = path; +} + +void clear_test_state() { + realm::_impl::RealmCoordinator::clear_all_caches(); + realm::remove_realm_files_from_directory(realm::default_realm_file_directory()); } } // js diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 26eb9c81..1c6ddc29 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -25,12 +25,13 @@ #include "js_types.hpp" #include "js_util.hpp" #include "js_object.hpp" +#include "js_list.hpp" +#include "js_results.hpp" #include "js_schema.hpp" #include "shared_realm.hpp" #include "binding_context.hpp" #include "object_accessor.hpp" -#include "results.hpp" #include "platform.hpp" namespace realm { @@ -99,10 +100,10 @@ class RealmDelegate : public BindingContext { std::string default_path(); void set_default_path(std::string path); +void clear_test_state(); template class Realm : public BindingContext { -public: using ContextType = typename T::Context; using FunctionType = typename T::Function; using ObjectType = typename T::Object; @@ -113,6 +114,7 @@ public: using ReturnValue = ReturnValue; using NativeAccessor = realm::NativeAccessor; + public: // member methods static void Objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); static void Create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); @@ -131,11 +133,26 @@ public: // constructor methods static void Constructor(ContextType, ObjectType, size_t, const ValueType[]); static void SchemaVersion(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void ClearTestState(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); // static properties static void GetDefaultPath(ContextType, ObjectType, ReturnValue &); static void SetDefaultPath(ContextType, ObjectType, ValueType value); + static ObjectType create_constructor(ContextType ctx) { + ObjectType realm_constructor = ObjectWrap::create_constructor(ctx); + ObjectType collection_constructor = ObjectWrap::create_constructor(ctx); + ObjectType list_constructor = ObjectWrap::create_constructor(ctx); + ObjectType results_constructor = ObjectWrap::create_constructor(ctx); + + PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); + Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); + Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes); + Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes); + + return realm_constructor; + } + static std::string validated_notification_name(ContextType ctx, const ValueType &value) { std::string name = Value::validated_to_string(ctx, value, "notification name"); if (name != "change") { @@ -150,8 +167,8 @@ public: FunctionType constructor = Value::to_constructor(ctx, value); auto delegate = get_delegate(realm.get()); - for (auto pair : delegate->m_constructors) { - if (pair.second == constructor) { + for (auto &pair : delegate->m_constructors) { + if (FunctionType(pair.second) == constructor) { return pair.first; } } @@ -176,6 +193,15 @@ struct ObjectClass : BaseObjectClass { ConstructorType* const constructor = Realm::Constructor; + MethodMap const static_methods = { + {"schemaVersion", wrap}, + {"clearTestState", wrap}, + }; + + PropertyMap const static_properties = { + {"defaultPath", {wrap, wrap}}, + }; + MethodMap const methods = { {"objects", wrap}, {"create", wrap}, @@ -259,8 +285,8 @@ void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, realm->m_binding_context.reset(delegate); } - delegate->m_defaults = defaults; - delegate->m_constructors = constructors; + delegate->m_defaults = std::move(defaults); + delegate->m_constructors = std::move(constructors); set_internal(this_object, new SharedRealm(realm)); } @@ -282,10 +308,16 @@ void Realm::SchemaVersion(ContextType ctx, ObjectType this_object, size_t arg return_value.set(-1); } else { - return_value.set(version); + return_value.set((double)version); } } +template +void Realm::ClearTestState(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + clear_test_state(); +} + template void Realm::GetDefaultPath(ContextType ctx, ObjectType object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); @@ -312,11 +344,10 @@ template void Realm::Objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); - SharedRealm sharedRealm = *get_internal(this_object); - std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); + SharedRealm realm = *get_internal(this_object); + std::string type = validated_object_type_for_value(realm, ctx, arguments[0]); - // TODO -// return_value = RJSResultsCreate(ctx, sharedRealm, className); + return_value.set(Results::create(ctx, realm, type)); } template @@ -357,7 +388,6 @@ void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, cons ObjectType arg = Value::validated_to_object(ctx, arguments[0]); - // TODO: fix this template call if (Object::template is_instance(ctx, arg)) { auto object = get_internal(arg); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); @@ -377,10 +407,10 @@ void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, cons table->move_last_over(realm_object->row().get_index()); } } -// else if(RJSValueIsObjectOfClass(ctx, arg, realm::js::results_class())) { -// auto results = get_internal(arg); -// results->clear(); -// } + else if (Object::template is_instance(ctx, arg)) { + auto results = get_internal(arg); + results->clear(); + } else if (Object::template is_instance(ctx, arg)) { auto list = get_internal(arg); list->delete_all(); diff --git a/src/js_results.cpp b/src/js_results.cpp deleted file mode 100644 index 0f5a4e43..00000000 --- a/src/js_results.cpp +++ /dev/null @@ -1,256 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_results.hpp" -#include "jsc_collection.hpp" -#include "js_object.hpp" -#include "jsc_util.hpp" -#include "object_accessor.hpp" -#include "results.hpp" -#include "parser.hpp" -#include "query_builder.hpp" - -using namespace realm; - -JSValueRef ResultsGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) { - try { - Results *results = RJSGetInternal(object); - std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr == "length") { - return JSValueMakeNumber(ctx, results->size()); - } - - auto row = results->get(RJSValidatedPositiveIndex(indexStr)); - if (!row.is_attached()) { - return JSValueMakeNull(ctx); - } - - return RJSObjectCreate(ctx, Object(results->get_realm(), results->get_object_schema(), row)); - } - catch (std::out_of_range &exp) { - // getters for nonexistent properties in JS should always return undefined - return JSValueMakeUndefined(ctx); - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - return NULL; - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - return NULL; - } -} - -bool ResultsSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef *jsException) { - try { - std::string indexStr = RJSStringForJSString(propertyName); - if (indexStr != "length") { - stot(RJSStringForJSString(propertyName)); - } - - // attempts to assign to 'length' or an index should throw an exception - if (jsException) { - *jsException = RJSMakeError(ctx, "Results objects are readonly"); - } - } - catch (std::invalid_argument &exp) { - // for stol failure this could be another property that is handled externally, so ignore - } - return false; -} - -void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { - Results *results = RJSGetInternal(object); - size_t size = results->size(); - - char str[32]; - for (size_t i = 0; i < size; i++) { - sprintf(str, "%zu", i); - JSStringRef name = JSStringCreateWithUTF8CString(str); - JSPropertyNameAccumulatorAddName(propertyNames, name); - JSStringRelease(name); - } -} - -JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Results *results = RJSGetInternal(thisObject); - validate_argument_count(argumentCount, 0); - - Results *copy = new Results(*results); - copy->set_live(false); - - return js::WrapObject(ctx, RJSResultsClass(), copy); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - } - return NULL; -} - -JSValueRef ResultsSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Results *results = RJSGetInternal(thisObject); - validate_argument_count(argumentCount, 1, 2); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - return RJSResultsCreateSorted(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - } - return NULL; -} - -JSValueRef ResultsFiltered(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) { - try { - Results *results = RJSGetInternal(thisObject); - validate_argument_count_at_least(argumentCount, 1); - - SharedRealm sharedRealm = *RJSGetInternal(thisObject); - return RJSResultsCreateFiltered(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments); - } - catch (std::exception &exp) { - if (jsException) { - *jsException = RJSMakeError(ctx, exp); - } - } - return NULL; -} - -JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className) { - TableRef table = ObjectStore::table_for_object_type(realm->read_group(), className); - auto object_schema = realm->config().schema->find(className); - if (object_schema == realm->config().schema->end()) { - throw std::runtime_error("Object type '" + className + "' not present in Realm."); - } - return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table)); -} - -JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className, std::string queryString, std::vector args) { - TableRef table = ObjectStore::table_for_object_type(realm->read_group(), className); - Query query = table->where(); - const Schema &schema = *realm->config().schema; - auto object_schema = schema.find(className); - if (object_schema == schema.end()) { - throw std::runtime_error("Object type '" + className + "' not present in Realm."); - } - parser::Predicate predicate = parser::parse(queryString); - query_builder::ArgumentConverter arguments(ctx, args); - query_builder::apply_predicate(query, predicate, arguments, schema, object_schema->name); - - return js::WrapObject(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query))); -} - -JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, bool live) { - Results *results = new Results(realm, objectSchema, std::move(query)); - results->set_live(live); - - return js::WrapObject(ctx, js::results_class(), results); -} - -JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) { - std::string queryString = RJSValidatedStringForValue(ctx, arguments[0], "predicate"); - std::vector args(argumentCount - 1); - for (size_t i = 1; i < argumentCount; i++) { - args[i-1] = arguments[i]; - } - - parser::Predicate predicate = parser::parse(queryString); - query_builder::ArgumentConverter queryArgs(ctx, args); - query_builder::apply_predicate(query, predicate, queryArgs, *realm->config().schema, objectSchema.name); - - return RJSResultsCreate(ctx, realm, objectSchema, std::move(query)); -} - -JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) { - size_t prop_count; - std::vector prop_names; - std::vector ascending; - - if (RJSIsValueArray(ctx, arguments[0])) { - validate_argument_count(argumentCount, 1, "Second argument is not allowed if passed an array of sort descriptors"); - - JSObjectRef js_prop_names = RJSValidatedValueToObject(ctx, arguments[0]); - prop_count = RJSValidatedListLength(ctx, js_prop_names); - if (!prop_count) { - throw std::invalid_argument("Sort descriptor array must not be empty"); - } - - prop_names.resize(prop_count); - ascending.resize(prop_count); - - for (unsigned int i = 0; i < prop_count; i++) { - JSValueRef val = RJSValidatedPropertyAtIndex(ctx, js_prop_names, i); - - if (RJSIsValueArray(ctx, val)) { - prop_names[i] = RJSValidatedStringForValue(ctx, RJSValidatedPropertyAtIndex(ctx, (JSObjectRef)val, 0)); - ascending[i] = !JSValueToBoolean(ctx, RJSValidatedPropertyAtIndex(ctx, (JSObjectRef)val, 1)); - } - else { - prop_names[i] = RJSValidatedStringForValue(ctx, val); - ascending[i] = true; - } - } - } - else { - validate_argument_count(argumentCount, 1, 2); - - prop_count = 1; - prop_names.push_back(RJSValidatedStringForValue(ctx, arguments[0])); - ascending.push_back(argumentCount == 1 ? true : !JSValueToBoolean(ctx, arguments[1])); - } - - std::vector columns(prop_count); - size_t index = 0; - - for (std::string prop_name : prop_names) { - const Property *prop = objectSchema.property_for_name(prop_name); - if (!prop) { - throw std::runtime_error("Property '" + prop_name + "' does not exist on object type '" + objectSchema.name + "'"); - } - columns[index++] = prop->table_column; - } - - Results *results = new Results(realm, objectSchema, std::move(query), {std::move(columns), std::move(ascending)}); - return js::WrapObject(ctx, js::results_class(), results); -} - -static const JSStaticFunction RJSResultsFuncs[] = { - {"snapshot", ResultsStaticCopy, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"sorted", ResultsSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"filtered", ResultsFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSResultsClass() { - static JSClassRef s_objectClass = RJSCreateWrapperClass("Results", ResultsGetProperty, ResultsSetProperty, RJSResultsFuncs, ResultsPropertyNames, RJSCollectionClass()); - return s_objectClass; -} - -namespace realm { -namespace js { -JSClassRef results_class() { return RJSResultsClass(); }; -} -} diff --git a/src/js_results.hpp b/src/js_results.hpp index 282d7b25..2488e5ce 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -18,21 +18,225 @@ #pragma once -#include -#include +#include "js_collection.hpp" +#include "js_object.hpp" -#include "jsc_types.hpp" +#include "results.hpp" +#include "list.hpp" +#include "parser.hpp" +#include "query_builder.hpp" namespace realm { - class ObjectSchema; - class Realm; - class Query; - typedef std::shared_ptr SharedRealm; +namespace js { + +template +struct Results { + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; + using Object = Object; + using Value = Value; + using ReturnValue = ReturnValue; + + static ObjectType create(ContextType, const realm::Results &, bool live = true); + static ObjectType create(ContextType, const realm::List &, bool live = true); + static ObjectType create(ContextType, SharedRealm, const std::string &type, bool live = true); + static ObjectType create(ContextType, SharedRealm, const ObjectSchema &, Query, bool live = true); + + template + static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]); + + template + static ObjectType create_sorted(ContextType, const U &, size_t, const ValueType[]); + + static void GetLength(ContextType, ObjectType, ReturnValue &); + static void GetIndex(ContextType, ObjectType, uint32_t, ReturnValue &); + + static void StaticResults(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); +}; + +template +struct ObjectClass : BaseObjectClass { + using Results = Results; + + std::string const name = "Results"; + + MethodMap const methods = { + {"snapshot", wrap}, + {"filtered", wrap}, + {"sorted", wrap}, + }; + + PropertyMap const properties = { + {"length", {wrap}}, + }; + + IndexPropertyType const index_accessor = {wrap}; +}; + +template +typename T::Object Results::create(ContextType ctx, const realm::Results &results, bool live) { + auto new_results = new realm::Results(results); + new_results->set_live(live); + + return create_object(ctx, new_results); } -JSClassRef RJSResultsClass(); -JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className); -JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className, std::string query, std::vector args); -JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, bool live = true); -JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, size_t argumentCount, const JSValueRef arguments[]); -JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, size_t argumentCount, const JSValueRef arguments[]); +template +typename T::Object Results::create(ContextType ctx, const realm::List &list, bool live) { + return create(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live); +} + +template +typename T::Object Results::create(ContextType ctx, SharedRealm realm, const std::string &type, bool live) { + auto table = ObjectStore::table_for_object_type(realm->read_group(), type); + auto &schema = realm->config().schema; + auto object_schema = schema->find(type); + + if (object_schema == schema->end()) { + throw std::runtime_error("Object type '" + type + "' not present in Realm."); + } + + auto results = new realm::Results(realm, *object_schema, *table); + results->set_live(live); + + return create_object(ctx, results); +} + +template +typename T::Object Results::create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { + auto results = new realm::Results(realm, object_schema, std::move(query)); + results->set_live(live); + + return create_object(ctx, results); +} + +template +template +typename T::Object Results::create_filtered(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { + auto query_string = Value::validated_to_string(ctx, arguments[0], "predicate"); + auto query = collection.get_query(); + auto const &realm = collection.get_realm(); + auto const &object_schema = collection.get_object_schema(); + + std::vector args; + args.reserve(argc - 1); + + for (size_t i = 1; i < argc; i++) { + args.push_back(arguments[i]); + } + + parser::Predicate predicate = parser::parse(query_string); + query_builder::ArgumentConverter converter(ctx, args); + query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name); + + return create(ctx, realm, object_schema, std::move(query)); +} + +template +template +typename T::Object Results::create_sorted(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { + auto const &realm = collection.get_realm(); + auto const &object_schema = collection.get_object_schema(); + std::vector prop_names; + std::vector ascending; + size_t prop_count; + + if (Value::is_array(ctx, arguments[0])) { + validate_argument_count(argc, 1, "Second argument is not allowed if passed an array of sort descriptors"); + + ObjectType js_prop_names = Value::validated_to_object(ctx, arguments[0]); + prop_count = Object::validated_get_length(ctx, js_prop_names); + if (!prop_count) { + throw std::invalid_argument("Sort descriptor array must not be empty"); + } + + prop_names.resize(prop_count); + ascending.resize(prop_count); + + for (unsigned int i = 0; i < prop_count; i++) { + ValueType value = Object::validated_get_property(ctx, js_prop_names, i); + + if (Value::is_array(ctx, value)) { + ObjectType array = Value::to_array(ctx, value); + prop_names[i] = Object::validated_get_string(ctx, array, 0); + ascending[i] = !Object::validated_get_boolean(ctx, array, 1); + } + else { + prop_names[i] = Value::validated_to_string(ctx, value); + ascending[i] = true; + } + } + } + else { + validate_argument_count(argc, 1, 2); + + prop_count = 1; + prop_names.push_back(Value::validated_to_string(ctx, arguments[0])); + ascending.push_back(argc == 1 ? true : !Value::to_boolean(ctx, arguments[1])); + } + + std::vector columns; + columns.reserve(prop_count); + + for (std::string &prop_name : prop_names) { + const Property *prop = object_schema.property_for_name(prop_name); + if (!prop) { + throw std::runtime_error("Property '" + prop_name + "' does not exist on object type '" + object_schema.name + "'"); + } + columns.push_back(prop->table_column); + } + + auto results = new realm::Results(realm, object_schema, collection.get_query(), {std::move(columns), std::move(ascending)}); + return create_object(ctx, results); +} + +template +void Results::GetLength(ContextType ctx, ObjectType object, ReturnValue &return_value) { + auto results = get_internal(object); + return_value.set((uint32_t)results->size()); +} + +template +void Results::GetIndex(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { + auto results = get_internal(object); + auto row = results->get(index); + + // Return null for deleted objects in a snapshot. + if (!row.is_attached()) { + return_value.set_null(); + return; + } + + auto realm_object = realm::Object(results->get_realm(), results->get_object_schema(), results->get(index)); + return_value.set(RealmObject::create(ctx, realm_object)); +} + +template +void Results::StaticResults(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + auto results = get_internal(this_object); + return_value.set(Results::create(ctx, *results, false)); +} + +template +void Results::Filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count_at_least(argc, 1); + + auto results = get_internal(this_object); + return_value.set(create_filtered(ctx, *results, argc, arguments)); +} + +template +void Results::Sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1, 2); + + auto results = get_internal(this_object); + return_value.set(create_sorted(ctx, *results, argc, arguments)); +} + +} // js +} // realm diff --git a/src/js_schema.hpp b/src/js_schema.hpp index 79a6aa49..c58938fd 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -178,7 +178,7 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc } else { auto propertyNames = Object::get_property_names(ctx, propertiesObject); - for (auto propertyName : propertyNames) { + for (auto &propertyName : propertyNames) { ValueType propertyValue = Object::get_property(ctx, propertiesObject, propertyName); objectSchema.properties.emplace_back(parse_property(ctx, propertyValue, propertyName, objectDefaults)); } diff --git a/src/js_types.hpp b/src/js_types.hpp index 696d74bc..5791b736 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -22,6 +22,8 @@ #include #include +#include + #if defined(__GNUC__) && !(defined(DEBUG) && DEBUG) # define REALM_JS_INLINE inline __attribute__((always_inline)) #elif defined(_MSC_VER) && !(defined(DEBUG) && DEBUG) @@ -128,12 +130,12 @@ class Function { using ValueType = typename T::Value; public: - static ValueType call(ContextType, const FunctionType &, const ObjectType &, uint32_t, const ValueType[]); + static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]); static ValueType call(ContextType ctx, const FunctionType &function, const ObjectType &this_object, const std::vector &arguments) { return call(ctx, function, this_object, arguments.size(), arguments.data()); } - static ObjectType construct(ContextType, const FunctionType &, uint32_t, const ValueType[]); + static ObjectType construct(ContextType, const FunctionType &, size_t, const ValueType[]); static ValueType construct(ContextType ctx, const FunctionType &function, const std::vector &arguments) { return construct(ctx, function, arguments.size(), arguments.data()); } @@ -147,6 +149,9 @@ class Object { using ValueType = typename T::Value; public: + static ValueType get_prototype(ContextType, const ObjectType &); + static void set_prototype(ContextType, const ObjectType &, const ValueType &); + static bool has_property(ContextType, const ObjectType &, const String &); static bool has_property(ContextType, const ObjectType &, uint32_t); static ValueType get_property(ContextType, const ObjectType &, const String &); @@ -155,8 +160,13 @@ class Object { static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &); static std::vector> get_property_names(ContextType, const ObjectType &); - static ValueType get_prototype(ContextType, const ObjectType &); - static void set_prototype(ContextType, const ObjectType &, const ValueType &); + template + static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) { + if (!has_property(ctx, object, property)) { + throw std::out_of_range(message ?: "Object missing expected property: " + util::to_string(property)); + } + return get_property(ctx, object, property); + } static uint32_t validated_get_length(ContextType ctx, const ObjectType &object) { static const String length_string = "length"; diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index ae577531..8e1b7b66 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -38,17 +38,8 @@ using IndexPropertySetterType = js::IndexPropertySetterType; using StringPropertyGetterType = js::StringPropertyGetterType; using StringPropertySetterType = js::StringPropertySetterType; using StringPropertyEnumeratorType = js::StringPropertyEnumeratorType; - -template -static inline T stot(const std::string &s) { - std::istringstream iss(s); - T value; - iss >> value; - if (iss.fail()) { - throw std::invalid_argument("Cannot convert string '" + s + "'"); - } - return value; -} +using MethodMap = js::MethodMap; +using PropertyMap = js::PropertyMap; template class ObjectWrap { @@ -59,7 +50,12 @@ class ObjectWrap { ObjectWrap(T* object = nullptr) : m_object(object) {} static JSObjectRef construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { - JSObjectRef this_object = ObjectWrap::create(ctx, nullptr); + if (!s_class.constructor) { + *exception = jsc::Exception::value(ctx, "Illegal constructor"); + return nullptr; + } + + JSObjectRef this_object = ObjectWrap::create(ctx); try { s_class.constructor(ctx, this_object, argc, arguments); } @@ -69,29 +65,52 @@ class ObjectWrap { return this_object; } + static bool has_instance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { + return JSValueIsObjectOfClass(ctx, value, get_class()); + } + static JSValueRef get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { if (auto index_getter = s_class.index_accessor.getter) { try { uint32_t index = validated_positive_index(jsc::String(property)); return index_getter(ctx, object, index, exception); } - catch (std::out_of_range &) {} - catch (std::invalid_argument &) {} + catch (std::out_of_range &) { + // Out-of-bounds index getters should just return undefined in JS. + return Value::from_undefined(ctx); + } + catch (std::invalid_argument &) { + // Property is not a number. + } } if (auto string_getter = s_class.string_accessor.getter) { return string_getter(ctx, object, property, exception); } - return NULL; + return nullptr; } static bool set_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { - if (auto index_setter = s_class.index_accessor.setter) { + auto index_setter = s_class.index_accessor.setter; + + if (index_setter || s_class.index_accessor.getter) { try { uint32_t index = validated_positive_index(jsc::String(property)); - return index_setter(ctx, object, index, value, exception); + + if (index_setter) { + return index_setter(ctx, object, index, value, exception); + } + else { + *exception = Exception::value(ctx, std::string("Cannot assigned to read only index ") + util::to_string(index)); + return false; + } + } + catch (std::out_of_range &e) { + *exception = Exception::value(ctx, e); + return false; + } + catch (std::invalid_argument &) { + // Property is not a number. } - catch (std::out_of_range &) {} - catch (std::invalid_argument &) {} } if (auto string_setter = s_class.string_accessor.setter) { return string_setter(ctx, object, property, value, exception); @@ -99,13 +118,18 @@ class ObjectWrap { return false; } + static bool set_readonly_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { + *exception = Exception::value(ctx, std::string("Cannot assign to read only property '") + std::string(String(property)) + "'"); + return false; + } + static void get_property_names(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { if (s_class.index_accessor.getter) { try { uint32_t length = Object::validated_get_length(ctx, object); char string[32]; for (uint32_t i = 0; i < length; i++) { - sprintf(string, "%lu", i); + sprintf(string, "%u", i); JSPropertyNameAccumulatorAddName(accumulator, jsc::String(string)); } } @@ -118,82 +142,131 @@ class ObjectWrap { } } - template - static JSClassRef get_superclass(BaseObjectClass*) { - return nullptr; + static void finalize(JSObjectRef object) { + // This is called for the most derived class before superclasses. + if (auto wrap = static_cast *>(JSObjectGetPrivate(object))) { + delete wrap; + JSObjectSetPrivate(object, nullptr); + } } + template static JSClassRef get_superclass(ObjectClass*) { return ObjectWrap::get_class(); } - static JSClassRef create_class() { - JSStaticFunction staticFunctions[s_class.methods.size() + 1]; - JSStaticValue staticValues[s_class.properties.size() + 1]; - JSClassDefinition definition; + static std::vector get_methods(const MethodMap &methods) { + std::vector functions; + functions.reserve(methods.size() + 1); - // TODO: Set parentClass ensuring finalize is setup to do the right thing. + JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : methods) { + functions[index++] = {pair.first.c_str(), pair.second, attributes}; + } + + functions[index] = {0}; + return functions; + } + + static std::vector get_properties(const PropertyMap &properties) { + std::vector values; + values.reserve(properties.size() + 1); + + JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : properties) { + auto &prop = pair.second; + values[index++] = {pair.first.c_str(), prop.getter, prop.setter ?: set_readonly_property, attributes}; + } + + values[index] = {0}; + return values; + } + + static JSClassRef create_class() { + JSClassDefinition definition = kJSClassDefinitionEmpty; + std::vector methods; + std::vector properties; + + definition.parentClass = get_superclass(s_class.superclass); + definition.className = s_class.name.c_str(); definition.finalize = finalize; - if (s_class.constructor) { - // TODO: Correctly setup constructor class with static methods/properties. - // definition.callAsConstructor = construct; + if (!s_class.methods.empty()) { + methods = get_methods(s_class.methods); + definition.staticFunctions = methods.data(); } + if (!s_class.properties.empty()) { + properties = get_properties(s_class.properties); + definition.staticValues = properties.data(); + } + if (s_class.index_accessor.getter || s_class.string_accessor.getter) { definition.getProperty = get_property; - } - if (s_class.index_accessor.setter || s_class.string_accessor.setter) { definition.setProperty = set_property; } + else if (s_class.index_accessor.setter || s_class.string_accessor.setter) { + definition.setProperty = set_property; + } + if (s_class.index_accessor.getter || s_class.string_accessor.enumerator) { definition.getPropertyNames = get_property_names; } - if (!s_class.methods.empty()) { - JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - size_t index = 0; + return JSClassCreate(&definition); + } - for (auto &pair : s_class.methods) { - staticFunctions[index++] = {pair.first.c_str(), pair.second, attributes}; - } - - staticFunctions[index] = {0}; - definition.staticFunctions = staticFunctions; + static JSClassRef create_constructor_class() { + // Skip creating a special constructor class if possible. + if (!s_class.constructor && s_class.static_methods.empty() && s_class.static_properties.empty()) { + return nullptr; } - if (!s_class.properties.empty()) { - JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - size_t index = 0; + JSClassDefinition definition = kJSClassDefinitionEmpty; + std::vector methods; + std::vector properties; - for (auto &pair : s_class.properties) { - auto &prop = pair.second; - staticValues[index++] = {pair.first.c_str(), prop.getter, prop.setter, attributes | (prop.setter ? 0 : kJSPropertyAttributeReadOnly)}; - } + definition.attributes = kJSClassAttributeNoAutomaticPrototype; + definition.className = s_class.name.c_str(); + definition.hasInstance = has_instance; - staticValues[index] = {0}; - definition.staticValues = staticValues; + if (s_class.constructor) { + definition.callAsConstructor = construct; + } + if (!s_class.static_methods.empty()) { + methods = get_methods(s_class.static_methods); + definition.staticFunctions = methods.data(); + } + if (!s_class.static_properties.empty()) { + properties = get_properties(s_class.static_properties); + definition.staticValues = properties.data(); } return JSClassCreate(&definition); } + public: + operator T*() const { + return m_object.get(); + } + ObjectWrap& operator=(T* object) { + if (m_object.get() != object) { + m_object = std::unique_ptr(object); + } + return *this; + } + static JSClassRef get_class() { static JSClassRef js_class = create_class(); return js_class; } - static void finalize(JSObjectRef object) { - delete static_cast *>(JSObjectGetPrivate(object)); - } - - static JSObjectRef uncallable_constructor(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { - *exception = jsc::Exception::value(ctx, "Illegal constructor"); - return NULL; - } - - public: - operator T*() const { - return m_object.get(); + static JSClassRef get_constructor_class() { + static JSClassRef js_class = create_constructor_class(); + return js_class; } static JSObjectRef create(JSContextRef ctx, T* internal = nullptr) { @@ -201,7 +274,11 @@ class ObjectWrap { } static JSObjectRef create_constructor(JSContextRef ctx) { - return JSObjectMakeConstructor(ctx, get_class(), uncallable_constructor); + if (JSClassRef constructor_class = get_constructor_class()) { + return JSObjectMake(ctx, constructor_class, nullptr); + } + + return JSObjectMakeConstructor(ctx, get_class(), construct); } static bool has_instance(JSContextRef ctx, JSValueRef value) { @@ -209,6 +286,12 @@ class ObjectWrap { } }; +// Make the top-level base class return a NULL JSClassRef. +template<> +inline JSClassRef ObjectWrap::get_class() { + return nullptr; +} + // The declared static variables must be defined as well. template ObjectClass ObjectWrap::s_class; @@ -216,6 +299,9 @@ template ObjectClass ObjectWrap::s_class; namespace js { +template +class ObjectWrap : public jsc::ObjectWrap {}; + template JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { jsc::ReturnValue return_value(ctx); @@ -241,13 +327,15 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSVa } template -void wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { +bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { try { F(ctx, object, value); + return true; } catch(std::exception &e) { *exception = jsc::Exception::value(ctx, e); } + return false; } template @@ -256,6 +344,10 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef try { F(ctx, object, index, return_value); } + catch (std::out_of_range &) { + // Out-of-bounds index getters should just return undefined in JS. + return jsc::Value::from_undefined(ctx); + } catch(std::exception &e) { *exception = jsc::Exception::value(ctx, e); } diff --git a/src/jsc/jsc_collection.cpp b/src/jsc/jsc_collection.cpp deleted file mode 100644 index 6af2b2a5..00000000 --- a/src/jsc/jsc_collection.cpp +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "jsc_collection.hpp" - -static JSClassRef RJSCreateCollectionClass() { - JSClassDefinition classDefinition = kJSClassDefinitionEmpty; - classDefinition.className = "Collection"; - return JSClassCreate(&classDefinition); -} - -JSClassRef RJSCollectionClass() { - static JSClassRef s_collectionClass = RJSCreateCollectionClass(); - return s_collectionClass; -} diff --git a/src/jsc/jsc_collection.hpp b/src/jsc/jsc_collection.hpp deleted file mode 100644 index f871a1c4..00000000 --- a/src/jsc/jsc_collection.hpp +++ /dev/null @@ -1,23 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsc_types.hpp" - -JSClassRef RJSCollectionClass(); diff --git a/src/jsc/jsc_init.cpp b/src/jsc/jsc_init.cpp index 537ce572..3a36ba6a 100644 --- a/src/jsc/jsc_init.cpp +++ b/src/jsc/jsc_init.cpp @@ -20,15 +20,8 @@ #include #include "jsc_init.hpp" -#include "jsc_realm.hpp" -#include "jsc_collection.hpp" -#include "jsc_list.hpp" -#include "jsc_util.hpp" #include "platform.hpp" -#include "shared_realm.hpp" -#include "impl/realm_coordinator.hpp" - extern "C" { using namespace realm; @@ -59,55 +52,23 @@ JSClassRef RJSRealmTypeClass() { return JSClassCreate(&realmTypesDefinition); } -static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { - RJSClearTestState(); - return NULL; -} - JSObjectRef RJSConstructorCreate(JSContextRef ctx) { - static const String clearTestStateString = "clearTestState"; - static const String collectionString = "Collection"; - static const String listString = "List"; - static const String resultsString = "Results"; - static const String typeString = "Types"; + JSObjectRef realm_constructor = js::Realm::create_constructor(ctx); + JSObjectRef types_object = JSObjectMake(ctx, RJSRealmTypeClass(), nullptr); - JSObjectRef realmObject = JSObjectMake(ctx, RJSRealmConstructorClass(), NULL); - js::PropertyAttributes attributes = js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete); + // TODO: Either remove this (preferable) or move implementation to JS. + jsc::Object::set_property(ctx, realm_constructor, "Types", types_object, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete)); -// JSObjectRef collectionConstructor = JSObjectMakeConstructor(ctx, RJSCollectionClass(), UncallableConstructor); -// RJSValidatedSetProperty(ctx, realmObject, collectionString, collectionConstructor, attributes); - -// JSObjectRef listConstructor = JSObjectMakeConstructor(ctx, RJSListClass(), UncallableConstructor); -// RJSValidatedSetProperty(ctx, realmObject, listString, listConstructor, attributes); - -// JSObjectRef resultsContructor = JSObjectMakeConstructor(ctx, RJSResultsClass(), UncallableConstructor); -// RJSValidatedSetProperty(ctx, realmObject, resultsString, resultsContructor, attributes); - - JSObjectRef typesObject = JSObjectMake(ctx, RJSRealmTypeClass(), NULL); - RJSValidatedSetProperty(ctx, realmObject, typeString, typesObject, attributes); - - JSObjectRef clearTestStateFunction = JSObjectMakeFunctionWithCallback(ctx, clearTestStateString, ClearTestState); - jsc::Object::set_property(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes); - - return realmObject; + return realm_constructor; } void RJSInitializeInContext(JSContextRef ctx) { - static const String nameString = "Realm"; + static const String realm_string = "Realm"; - JSObjectRef globalObject = JSContextGetGlobalObject(ctx); - JSObjectRef realmObject = RJSConstructorCreate(ctx); + JSObjectRef global_object = JSContextGetGlobalObject(ctx); + JSObjectRef realm_constructor = RJSConstructorCreate(ctx); - JSValueRef exception = NULL; - JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - - JSObjectSetProperty(ctx, globalObject, nameString, realmObject, attributes, &exception); - assert(!exception); + jsc::Object::set_property(ctx, global_object, realm_string, realm_constructor, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete)); } -void RJSClearTestState() { - realm::_impl::RealmCoordinator::clear_all_caches(); - realm::remove_realm_files_from_directory(realm::default_realm_file_directory()); -} - -} +} // extern "C" diff --git a/src/jsc/jsc_init.hpp b/src/jsc/jsc_init.hpp index aa47aa82..c1621286 100644 --- a/src/jsc/jsc_init.hpp +++ b/src/jsc/jsc_init.hpp @@ -20,3 +20,4 @@ #include "jsc_init.h" #include "jsc_object_accessor.hpp" +#include "js_realm.hpp" diff --git a/src/jsc/jsc_list.cpp b/src/jsc/jsc_list.cpp deleted file mode 100644 index 232f51fc..00000000 --- a/src/jsc/jsc_list.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "jsc_class.hpp" -#include "jsc_list.hpp" -#include "jsc_collection.hpp" -#include "jsc_util.hpp" -#include "js_list.hpp" - -#include - -using RJSAccessor = realm::NativeAccessor; -using namespace realm; - -void ListPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { - List *list = js::get_internal(object); - size_t size = list->size(); - - char str[32]; - for (size_t i = 0; i < size; i++) { - sprintf(str, "%zu", i); - JSStringRef name = JSStringCreateWithUTF8CString(str); - JSPropertyNameAccumulatorAddName(propertyNames, name); - JSStringRelease(name); - } -} - -using RJSList = js::List; -WRAP_PROPERTY_GETTER(RJSList, GetLength) -WRAP_INDEXED_GETTER(RJSList, GetIndex) -WRAP_INDEXED_SETTER(RJSList, SetIndex) -WRAP_CLASS_METHOD(RJSList, Push) -WRAP_CLASS_METHOD(RJSList, Pop) -WRAP_CLASS_METHOD(RJSList, Unshift) -WRAP_CLASS_METHOD(RJSList, Shift) -WRAP_CLASS_METHOD(RJSList, Splice) -WRAP_CLASS_METHOD(RJSList, StaticResults) -WRAP_CLASS_METHOD(RJSList, Filtered) -WRAP_CLASS_METHOD(RJSList, Sorted) - -JSObjectRef RJSListCreate(JSContextRef ctx, List &list) { - return realm::js::WrapObject(ctx, realm::js::list_class(), new List(list)); -} - -static const JSStaticFunction RJSListFuncs[] = { - {"push", RJSListPush, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"pop", RJSListPop, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"shift", RJSListShift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"unshift", RJSListUnshift, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"splice", RJSListSplice, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"filtered", RJSListFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"sorted", RJSListSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"snapshot", RJSListStaticResults, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -static const JSStaticValue RJSListProps[] = { - {"length", RJSListGetLength, nullptr, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSListClass() { - static JSClassRef s_listClass = RJSCreateWrapperClass("List", RJSListGetIndex, RJSListSetIndex, RJSListFuncs, ListPropertyNames, RJSCollectionClass(), RJSListProps); - return s_listClass; -} - -JSClassRef realm::js::list_class() { return RJSListClass(); }; diff --git a/src/jsc/jsc_realm.cpp b/src/jsc/jsc_realm.cpp deleted file mode 100644 index f23a74ce..00000000 --- a/src/jsc/jsc_realm.cpp +++ /dev/null @@ -1,111 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "jsc_class.hpp" -#include "jsc_realm.hpp" -#include "js_realm.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "jsc_list.hpp" -#include "js_schema.hpp" -#include "jsc_util.hpp" -#include "platform.hpp" - -#include "shared_realm.hpp" -#include "impl/realm_coordinator.hpp" -#include "object_accessor.hpp" -#include "binding_context.hpp" -#include "results.hpp" - -#include - -using namespace realm; -using RJSAccessor = realm::NativeAccessor; - -bool RealmHasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { - return JSValueIsObjectOfClass(ctx, value, RJSRealmClass()); -} - -using RJSRealm = realm::js::Realm; -WRAP_CONSTRUCTOR(RJSRealm, Constructor); -WRAP_CLASS_METHOD(RJSRealm, SchemaVersion) -WRAP_PROPERTY_GETTER(RJSRealm, GetDefaultPath) -WRAP_PROPERTY_SETTER(RJSRealm, SetDefaultPath) - -static const JSStaticValue RealmConstructorStaticProperties[] = { - {"defaultPath", RJSRealmGetDefaultPath, RJSRealmSetDefaultPath, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} -}; - -static const JSStaticFunction RealmConstructorFuncs[] = { - {"schemaVersion", RJSRealmSchemaVersion, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmConstructorClass() { - JSClassDefinition realmConstructorDefinition = kJSClassDefinitionEmpty; - realmConstructorDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; - realmConstructorDefinition.className = "RealmConstructor"; - realmConstructorDefinition.callAsConstructor = RJSRealmConstructor; - realmConstructorDefinition.hasInstance = RealmHasInstance; - realmConstructorDefinition.staticValues = RealmConstructorStaticProperties; - realmConstructorDefinition.staticFunctions = RealmConstructorFuncs; - return JSClassCreate(&realmConstructorDefinition); -} - -WRAP_CLASS_METHOD(RJSRealm, Objects) -WRAP_CLASS_METHOD(RJSRealm, Create) -WRAP_CLASS_METHOD(RJSRealm, Delete) -WRAP_CLASS_METHOD(RJSRealm, DeleteAll) -WRAP_CLASS_METHOD(RJSRealm, Write) -WRAP_CLASS_METHOD(RJSRealm, AddListener) -WRAP_CLASS_METHOD(RJSRealm, RemoveListener) -WRAP_CLASS_METHOD(RJSRealm, RemoveAllListeners) -WRAP_CLASS_METHOD(RJSRealm, Close) -WRAP_PROPERTY_GETTER(RJSRealm, GetPath) -WRAP_PROPERTY_GETTER(RJSRealm, GetSchemaVersion) - -static const JSStaticValue RealmStaticProperties[] = { - {"path", RJSRealmGetPath, NULL, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"schemaVersion", RJSRealmGetSchemaVersion, NULL, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL} -}; - -static const JSStaticFunction RJSRealmFuncs[] = { - {"objects", RJSRealmObjects, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"create", RJSRealmCreate, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"delete", RJSRealmDelete, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"deleteAll", RJSRealmDeleteAll, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"write", RJSRealmWrite, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"addListener", RJSRealmAddListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeListener", RJSRealmRemoveListener, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"removeAllListeners", RJSRealmRemoveAllListeners, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {"close", RJSRealmClose, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, - {NULL, NULL}, -}; - -JSClassRef RJSRealmClass() { - static JSClassRef s_realmClass = RJSCreateWrapperClass("Realm", NULL, NULL, RJSRealmFuncs, NULL, NULL, RealmStaticProperties); - return s_realmClass; -} - -namespace realm { -namespace js { -JSClassRef realm_class() { return RJSRealmClass(); }; -} -} diff --git a/src/jsc/jsc_realm.hpp b/src/jsc/jsc_realm.hpp deleted file mode 100644 index b08aae73..00000000 --- a/src/jsc/jsc_realm.hpp +++ /dev/null @@ -1,24 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsc_types.hpp" - -JSClassRef RJSRealmClass(); -JSClassRef RJSRealmConstructorClass(); diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index e7346404..470aed2b 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -22,8 +22,6 @@ #include #include -#include - #include "js_types.hpp" namespace realm { @@ -42,10 +40,11 @@ struct Types { using FunctionCallback = JSObjectCallAsFunctionCallback; using PropertyGetterCallback = JSObjectGetPropertyCallback; using PropertySetterCallback = JSObjectSetPropertyCallback; - using IndexPropertyGetterCallback = JSValueRef(JSContextRef, JSObjectRef, uint32_t, JSValueRef*); - using IndexPropertySetterCallback = bool(JSContextRef, JSObjectRef, uint32_t, JSValueRef, JSValueRef*); + using IndexPropertyGetterCallback = JSValueRef (*)(JSContextRef, JSObjectRef, uint32_t, JSValueRef*); + using IndexPropertySetterCallback = bool (*)(JSContextRef, JSObjectRef, uint32_t, JSValueRef, JSValueRef*); using StringPropertyGetterCallback = JSObjectGetPropertyCallback; using StringPropertySetterCallback = JSObjectSetPropertyCallback; + using StringPropertyEnumeratorCallback = JSObjectGetPropertyNamesCallback; }; template @@ -92,14 +91,23 @@ namespace js { template<> class String { - const JSStringRef m_str; + using StringType = String; + + JSStringRef m_str; public: String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {} String(const JSStringRef &s) : m_str(JSStringRetain(s)) {} - String(JSStringRef &&s) : m_str(s) {} String(const std::string &str) : String(str.c_str()) {} - ~String() { JSStringRelease(m_str); } + String(const StringType &o) : String(o.m_str) {} + String(StringType &&o) : m_str(o.m_str) { + o.m_str = nullptr; + } + ~String() { + if (m_str) { + JSStringRelease(m_str); + } + } operator JSStringRef() const { return m_str; @@ -180,7 +188,8 @@ class Protected : public Protected { Protected(JSContextRef ctx, JSObjectRef object) : Protected(ctx, object) {} operator JSObjectRef() const { - return static_cast(*this); + JSValueRef value = static_cast(*this); + return (JSObjectRef)value; } }; @@ -270,27 +279,27 @@ inline bool jsc::Value::is_valid(const JSValueRef &value) { } template<> -JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) { +inline JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) { return JSValueMakeBoolean(ctx, boolean); } template<> -JSValueRef jsc::Value::from_null(JSContextRef ctx) { +inline JSValueRef jsc::Value::from_null(JSContextRef ctx) { return JSValueMakeNull(ctx); } template<> -JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) { +inline JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) { return JSValueMakeNumber(ctx, number); } template<> -JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) { +inline JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) { return JSValueMakeString(ctx, string); } template<> -JSValueRef jsc::Value::from_undefined(JSContextRef ctx) { +inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) { return JSValueMakeUndefined(ctx); } @@ -316,6 +325,10 @@ template<> inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) { JSValueRef exception = nullptr; jsc::String string = JSValueToStringCopy(ctx, value, &exception); + + // Since the string's retain value is +2 here, we need to manually release it before returning. + JSStringRelease(string); + if (exception) { throw jsc::Exception(ctx, exception); } @@ -353,7 +366,7 @@ inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &v } template<> -inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, uint32_t argc, const JSValueRef arguments[]) { +inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, size_t argc, const JSValueRef arguments[]) { JSValueRef exception = nullptr; JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception); if (exception) { @@ -363,7 +376,7 @@ inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &funct } template<> -inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, uint32_t argc, const JSValueRef arguments[]) { +inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, size_t argc, const JSValueRef arguments[]) { JSValueRef exception = nullptr; JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception); if (exception) { diff --git a/src/jsc/jsc_util.hpp b/src/jsc/jsc_util.hpp deleted file mode 100644 index b1fa4a24..00000000 --- a/src/jsc/jsc_util.hpp +++ /dev/null @@ -1,303 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include -#include - -#include "jsc_types.hpp" - -#include "property.hpp" -#include "schema.hpp" - -#define WRAP_EXCEPTION(METHOD, EXCEPTION, ARGS...) \ -try { METHOD(ARGS); } \ -catch(std::exception &e) { RJSSetException(ctx, EXCEPTION, e); } - -#define WRAP_CLASS_METHOD(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, thisObject, argumentCount, arguments, returnValue); \ - return returnValue; \ -} - -#define WRAP_CONSTRUCTOR(CLASS_NAME, METHOD_NAME) \ -JSObjectRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* ex) { \ - JSObjectRef returnObject = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, constructor, argumentCount, arguments, returnObject); \ - return returnObject; \ -} - -#define WRAP_PROPERTY_GETTER(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, property, returnValue); \ - return returnValue; \ -} - -#define WRAP_PROPERTY_SETTER(CLASS_NAME, METHOD_NAME) \ -bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ - WRAP_EXCEPTION(CLASS_NAME::METHOD_NAME, *ex, ctx, object, property, value); \ - return true; \ -} - -// for stol failure (std::invalid_argument) this could be another property that is handled externally, so ignore -#define WRAP_INDEXED_GETTER(CLASS_NAME, METHOD_NAME) \ -JSValueRef CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* ex) { \ - JSValueRef returnValue = NULL; \ - try { \ - size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ - CLASS_NAME::METHOD_NAME(ctx, object, index, returnValue); return returnValue; \ - } \ - catch (std::out_of_range &exp) { return JSValueMakeUndefined(ctx); } \ - catch (std::invalid_argument &exp) { return NULL; } \ - catch (std::exception &e) { RJSSetException(ctx, *ex, e); return NULL; } \ -} - -#define WRAP_INDEXED_SETTER(CLASS_NAME, METHOD_NAME) \ -bool CLASS_NAME ## METHOD_NAME(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* ex) { \ - try { \ - size_t index = RJSValidatedPositiveIndex(RJSStringForJSString(property)); \ - { CLASS_NAME::METHOD_NAME(ctx, object, index, value); return true; } \ - } \ - catch (std::out_of_range &exp) { RJSSetException(ctx, *ex, exp); } \ - catch (std::invalid_argument &exp) { *ex = RJSMakeError(ctx, "Invalid index"); } \ - catch (std::exception &e) { RJSSetException(ctx, *ex, e); } \ - return false; \ -} - - -template -inline void RJSFinalize(JSObjectRef object) { - delete static_cast(JSObjectGetPrivate(object)); - JSObjectSetPrivate(object, NULL); -} - -template -inline T RJSGetInternal(JSObjectRef jsObject) { - return static_cast(JSObjectGetPrivate(jsObject)); -} - -template -JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL, - JSObjectGetPropertyNamesCallback propertyNames = NULL, JSClassRef parentClass = NULL, const JSStaticValue *values = NULL) { - JSClassDefinition classDefinition = kJSClassDefinitionEmpty; - classDefinition.className = name; - classDefinition.finalize = RJSFinalize; - classDefinition.getProperty = getter; - classDefinition.setProperty = setter; - classDefinition.staticFunctions = funcs; - classDefinition.getPropertyNames = propertyNames; - classDefinition.parentClass = parentClass; - classDefinition.staticValues = values; - return JSClassCreate(&classDefinition); -} - -std::string RJSStringForJSString(JSStringRef jsString); -std::string RJSStringForValue(JSContextRef ctx, JSValueRef value); -std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name = nullptr); - -JSStringRef RJSStringForString(const std::string &str); -JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str); - -class RJSException : public std::runtime_error { -public: - RJSException(JSContextRef ctx, JSValueRef &ex) : std::runtime_error(RJSStringForValue(ctx, ex)), - m_jsException(ex) {} - JSValueRef exception() { return m_jsException; } - -private: - JSValueRef m_jsException; -}; - -JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp); -JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp); -JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message); - -bool RJSIsValueArray(JSContextRef ctx, JSValueRef value); -bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value); -bool RJSIsValueDate(JSContextRef ctx, JSValueRef value); - -static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - JSObjectRef object = JSValueToObject(ctx, value, NULL); - if (!object) { - throw std::runtime_error(message ?: "Value is not an object."); - } - return object; -} - -static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - JSObjectRef object = JSValueToObject(ctx, value, NULL); - if (!object || !RJSIsValueDate(ctx, object)) { - throw std::runtime_error(message ?: "Value is not a date."); - } - return object; -} - -static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = nullptr) { - 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) { - if (JSValueIsNull(ctx, value)) { - throw std::invalid_argument("`null` is not a number."); - } - - JSValueRef exception = NULL; - double number = JSValueToNumber(ctx, value, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - if (isnan(number)) { - throw std::invalid_argument("Value not convertible to a number."); - } - return number; -} - -static inline double RJSValidatedValueToBoolean(JSContextRef ctx, JSValueRef value, const char *err = nullptr) { - if (!JSValueIsBoolean(ctx, value)) { - throw std::invalid_argument(err ?: "Value is not a boolean."); - } - return JSValueToBoolean(ctx, value); -} - -static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return propertyValue; -} - -static inline JSValueRef RJSValidatedPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return propertyValue; -} - -static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = nullptr) { - JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property); - if (JSValueIsUndefined(ctx, propertyValue)) { - throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined"); - } - return RJSValidatedValueToObject(ctx, propertyValue, err); -} - -static inline JSObjectRef RJSValidatedObjectAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) { - return RJSValidatedValueToObject(ctx, RJSValidatedPropertyAtIndex(ctx, object, index)); -} - -static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property) { - JSValueRef exception = NULL; - JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - return RJSValidatedStringForValue(ctx, propertyValue, RJSStringForJSString(property).c_str()); -} - -static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object) { - JSValueRef exception = NULL; - static JSStringRef lengthString = JSStringCreateWithUTF8CString("length"); - JSValueRef lengthValue = JSObjectGetProperty(ctx, object, lengthString, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - if (!JSValueIsNumber(ctx, lengthValue)) { - throw std::runtime_error("Missing property 'length'"); - } - - return RJSValidatedValueToNumber(ctx, lengthValue); -} - -static inline void RJSValidatedSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes = 0) { - JSValueRef exception = NULL; - JSObjectSetProperty(ctx, object, propertyName, value, attributes, &exception); - if (exception) { - throw RJSException(ctx, exception); - } -} - -template -T stot(const std::string s) { - std::istringstream iss(s); - T value; - iss >> value; - if (iss.fail()) { - throw std::invalid_argument("Cannot convert string '" + s + "'"); - } - return value; -} - -static inline size_t RJSValidatedPositiveIndex(std::string indexStr) { - long index = stot(indexStr); - if (index < 0) { - throw std::out_of_range(std::string("Index ") + indexStr + " cannot be less than zero."); - } - return index; -} - -static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JSStringRef type) { - JSObjectRef globalObject = JSContextGetGlobalObject(ctx); - - JSValueRef exception = NULL; - JSValueRef constructorValue = JSObjectGetProperty(ctx, globalObject, type, &exception); - if (exception) { - throw RJSException(ctx, exception); - } - - bool ret = JSValueIsInstanceOfConstructor(ctx, value, RJSValidatedValueToObject(ctx, constructorValue), &exception); - if (exception) { - throw RJSException(ctx, exception); - } - - return ret; -} - -static inline void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) { - returnObject = JSValueMakeUndefined(ctx); -} - -template -static inline void RJSSetReturnNumber(JSContextRef ctx, JSValueRef &returnObject, T number) { - returnObject = JSValueMakeNumber(ctx, number); -} - -static inline void RJSSetReturnArray(JSContextRef ctx, size_t count, const JSValueRef *objects, JSValueRef &returnObject) { - returnObject = JSObjectMakeArray(ctx, count, objects, NULL); -} - -static inline void RJSSetException(JSContextRef ctx, JSValueRef &exceptionObject, std::exception &exception) { - exceptionObject = RJSMakeError(ctx, exception); -} - -static bool RJSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) { - return JSValueIsObjectOfClass(ctx, value, jsClass); -} diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index b85141bb..7a19ad68 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -178,6 +178,9 @@ template Nan::Persistent ObjectWrap::s_temp namespace js { +template +class ObjectWrap : public node::ObjectWrap {}; + template void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); diff --git a/src/node/node_init.cpp b/src/node/node_init.cpp index 4ba03030..d9bf5cfa 100644 --- a/src/node/node_init.cpp +++ b/src/node/node_init.cpp @@ -17,8 +17,6 @@ //////////////////////////////////////////////////////////////////////////// #include "node_init.hpp" -#include "js_realm.hpp" -#include "js_list.hpp" namespace realm { namespace node { diff --git a/src/node/node_init.hpp b/src/node/node_init.hpp index ed47542c..8a921da4 100644 --- a/src/node/node_init.hpp +++ b/src/node/node_init.hpp @@ -19,3 +19,4 @@ #pragma once #include "node_object_accessor.hpp" +#include "js_realm.hpp" diff --git a/src/node/node_types.hpp b/src/node/node_types.hpp index 563bc17a..b00b4d87 100644 --- a/src/node/node_types.hpp +++ b/src/node/node_types.hpp @@ -311,9 +311,9 @@ inline v8::Local node::Value::to_constructor(v8::Isolate* isolate, } template<> -inline v8::Local node::Function::call(v8::Isolate* isolate, const v8::Local &function, const v8::Local &this_object, uint32_t argc, const v8::Local arguments[]) { +inline v8::Local node::Function::call(v8::Isolate* isolate, const v8::Local &function, const v8::Local &this_object, size_t argc, const v8::Local arguments[]) { Nan::TryCatch trycatch; - auto result = Nan::Call(function, this_object, argc, const_cast*>(arguments)); + auto result = Nan::Call(function, this_object, (int)argc, const_cast*>(arguments)); if (trycatch.HasCaught()) { throw node::Exception(isolate, trycatch.Exception()); @@ -322,9 +322,9 @@ inline v8::Local node::Function::call(v8::Isolate* isolate, const v8: } template<> -inline v8::Local node::Function::construct(v8::Isolate* isolate, const v8::Local &function, uint32_t argc, const v8::Local arguments[]) { +inline v8::Local node::Function::construct(v8::Isolate* isolate, const v8::Local &function, size_t argc, const v8::Local arguments[]) { Nan::TryCatch trycatch; - auto result = Nan::NewInstance(function, argc, const_cast*>(arguments)); + auto result = Nan::NewInstance(function, (int)argc, const_cast*>(arguments)); if (trycatch.HasCaught()) { throw node::Exception(isolate, trycatch.Exception()); diff --git a/src/rpc.cpp b/src/rpc.cpp index 89494d07..f3320d33 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -16,26 +16,27 @@ // //////////////////////////////////////////////////////////////////////////// -#include "rpc.hpp" - +#include #include #include #include -#include "jsc_init.h" + +#include "rpc.hpp" + +#include "jsc_init.hpp" #include "js_object.hpp" #include "js_results.hpp" -#include "jsc_list.hpp" #include "js_realm.hpp" -#include "js_util.hpp" #include "base64.hpp" #include "object_accessor.hpp" #include "shared_realm.hpp" #include "results.hpp" -#include - -using RJSAccessor = realm::NativeAccessor; -using namespace realm_js; + +using namespace realm; +using namespace realm::rpc; + +using Accessor = NativeAccessor; static const char * const RealmObjectTypesData = "data"; static const char * const RealmObjectTypesDate = "date"; @@ -59,9 +60,8 @@ RPCServer::RPCServer() { m_requests["/create_session"] = [this](const json dict) { RJSInitializeInContext(m_context); - JSStringRef realm_string = RJSStringForString("Realm"); - JSObjectRef realm_constructor = RJSValidatedObjectProperty(m_context, JSContextGetGlobalObject(m_context), realm_string); - JSStringRelease(realm_string); + jsc::String realm_string = "Realm"; + JSObjectRef realm_constructor = jsc::Object::validated_get_constructor(m_context, JSContextGetGlobalObject(m_context), realm_string); m_session_id = store_object(realm_constructor); return (json){{"result", m_session_id}}; @@ -80,88 +80,72 @@ RPCServer::RPCServer() { arg_values[i] = deserialize_json_value(args[i]); } - JSValueRef exception = NULL; - JSObjectRef realm_object = JSObjectCallAsConstructor(m_context, realm_constructor, arg_count, arg_values, &exception); - if (exception) { - return (json){{"error", RJSStringForValue(m_context, exception)}}; - } - + JSObjectRef realm_object = jsc::Function::construct(m_context, realm_constructor, arg_count, arg_values); RPCObjectID realm_id = store_object(realm_object); return (json){{"result", realm_id}}; }; m_requests["/begin_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - RJSGetInternal(m_objects[realm_id])->get()->begin_transaction(); + SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + + realm->begin_transaction(); return json::object(); }; m_requests["/cancel_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - RJSGetInternal(m_objects[realm_id])->get()->cancel_transaction(); + SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + + realm->cancel_transaction(); return json::object(); }; m_requests["/commit_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - RJSGetInternal(m_objects[realm_id])->get()->commit_transaction(); + SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + + realm->commit_transaction(); return json::object(); }; m_requests["/call_method"] = [this](const json dict) { JSObjectRef object = m_objects[dict["id"].get()]; - JSStringRef method_string = RJSStringForString(dict["name"].get()); - JSObjectRef function = RJSValidatedObjectProperty(m_context, object, method_string); - JSStringRelease(method_string); + std::string method_string = dict["name"].get(); + JSObjectRef function = jsc::Object::validated_get_function(m_context, object, method_string); json args = dict["arguments"]; - size_t count = args.size(); - JSValueRef arg_values[count]; - for (size_t i = 0; i < count; i++) { + size_t arg_count = args.size(); + JSValueRef arg_values[arg_count]; + for (size_t i = 0; i < arg_count; i++) { arg_values[i] = deserialize_json_value(args[i]); } - JSValueRef exception = NULL; - JSValueRef result = JSObjectCallAsFunction(m_context, function, object, count, arg_values, &exception); - if (exception) { - return (json){{"error", RJSStringForValue(m_context, exception)}}; - } + JSValueRef result = jsc::Function::call(m_context, function, object, arg_count, arg_values); return (json){{"result", serialize_json_value(result)}}; }; m_requests["/get_property"] = [this](const json dict) { RPCObjectID oid = dict["id"].get(); json name = dict["name"]; - JSValueRef value = NULL; - JSValueRef exception = NULL; + JSValueRef value; if (name.is_number()) { - value = JSObjectGetPropertyAtIndex(m_context, m_objects[oid], name.get(), &exception); + value = jsc::Object::get_property(m_context, m_objects[oid], name.get()); } else { - JSStringRef prop_string = RJSStringForString(name.get()); - value = JSObjectGetProperty(m_context, m_objects[oid], prop_string, &exception); - JSStringRelease(prop_string); + value = jsc::Object::get_property(m_context, m_objects[oid], name.get()); } - if (exception) { - return (json){{"error", RJSStringForValue(m_context, exception)}}; - } return (json){{"result", serialize_json_value(value)}}; }; m_requests["/set_property"] = [this](const json dict) { RPCObjectID oid = dict["id"].get(); json name = dict["name"]; JSValueRef value = deserialize_json_value(dict["value"]); - JSValueRef exception = NULL; if (name.is_number()) { - JSObjectSetPropertyAtIndex(m_context, m_objects[oid], name.get(), value, &exception); + jsc::Object::set_property(m_context, m_objects[oid], name.get(), value); } else { - JSStringRef prop_string = RJSStringForString(name.get()); - JSObjectSetProperty(m_context, m_objects[oid], prop_string, value, 0, &exception); - JSStringRelease(prop_string); + jsc::Object::set_property(m_context, m_objects[oid], name.get(), value); } - if (exception) { - return (json){{"error", RJSStringForValue(m_context, exception)}}; - } return json::object(); }; m_requests["/dispose_object"] = [this](const json dict) { @@ -181,7 +165,7 @@ RPCServer::RPCServer() { m_objects.erase(object.first); } JSGarbageCollect(m_context); - RJSClearTestState(); + js::clear_test_state(); return json::object(); }; } @@ -206,7 +190,7 @@ json RPCServer::perform_request(std::string name, json &args) { return {{"error", "Invalid session ID"}}; } } catch (std::exception &exception) { - return {{"error", (std::string)"exception thrown: " + exception.what()}}; + return {{"error", exception.what()}}; } } @@ -218,34 +202,34 @@ RPCObjectID RPCServer::store_object(JSObjectRef object) { return next_id; } -json RPCServer::serialize_json_value(JSValueRef value) { - switch (JSValueGetType(m_context, value)) { +json RPCServer::serialize_json_value(JSValueRef js_value) { + switch (JSValueGetType(m_context, js_value)) { case kJSTypeUndefined: return json::object(); case kJSTypeNull: return {{"value", json(nullptr)}}; case kJSTypeBoolean: - return {{"value", JSValueToBoolean(m_context, value)}}; + return {{"value", jsc::Value::to_boolean(m_context, js_value)}}; case kJSTypeNumber: - return {{"value", JSValueToNumber(m_context, value, NULL)}}; + return {{"value", jsc::Value::to_number(m_context, js_value)}}; case kJSTypeString: - return {{"value", RJSStringForValue(m_context, value)}}; + return {{"value", jsc::Value::to_string(m_context, js_value)}}; case kJSTypeObject: break; } - JSObjectRef js_object = JSValueToObject(m_context, value, NULL); + JSObjectRef js_object = jsc::Value::validated_to_object(m_context, js_value); - if (JSValueIsObjectOfClass(m_context, value, RJSObjectClass())) { - realm::Object *object = RJSGetInternal(js_object); + if (jsc::Object::is_instance(m_context, js_object)) { + auto object = jsc::Object::get_internal(js_object); return { {"type", RealmObjectTypesObject}, {"id", store_object(js_object)}, {"schema", serialize_object_schema(object->get_object_schema())} }; } - else if (JSValueIsObjectOfClass(m_context, value, RJSListClass())) { - realm::List *list = RJSGetInternal(js_object); + else if (jsc::Object::is_instance(m_context, js_object)) { + auto list = jsc::Object::get_internal(js_object); return { {"type", RealmObjectTypesList}, {"id", store_object(js_object)}, @@ -253,8 +237,8 @@ json RPCServer::serialize_json_value(JSValueRef value) { {"schema", serialize_object_schema(list->get_object_schema())} }; } - else if (JSValueIsObjectOfClass(m_context, value, RJSResultsClass())) { - realm::Results *results = RJSGetInternal(js_object); + else if (jsc::Object::is_instance(m_context, js_object)) { + auto results = jsc::Object::get_internal(js_object); return { {"type", RealmObjectTypesResults}, {"id", store_object(js_object)}, @@ -262,44 +246,40 @@ json RPCServer::serialize_json_value(JSValueRef value) { {"schema", serialize_object_schema(results->get_object_schema())} }; } - else if (RJSIsValueArray(m_context, value)) { - size_t length = RJSValidatedListLength(m_context, js_object); + else if (jsc::Value::is_array(m_context, js_object)) { + uint32_t length = jsc::Object::validated_get_length(m_context, js_object); std::vector array; - for (unsigned int i = 0; i < length; i++) { - array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(m_context, js_object, i, NULL))); + for (uint32_t i = 0; i < length; i++) { + array.push_back(serialize_json_value(jsc::Object::get_property(m_context, js_object, i))); } return {{"value", array}}; } - else if (RJSIsValueArrayBuffer(m_context, value)) { - std::string data = RJSAccessor::to_binary(m_context, value); + else if (jsc::Value::is_array_buffer(m_context, js_object)) { + std::string data = Accessor::to_binary(m_context, js_value); return { {"type", RealmObjectTypesData}, {"value", base64_encode((unsigned char *)data.data(), data.size())}, }; } - else if (RJSIsValueDate(m_context, value)) { + else if (jsc::Value::is_date(m_context, js_object)) { return { {"type", RealmObjectTypesDate}, - {"value", RJSValidatedValueToNumber(m_context, value)}, + {"value", jsc::Value::to_number(m_context, js_object)}, }; } else { // Serialize this JS object as a plain object since it doesn't match any known types above. - JSPropertyNameArrayRef js_keys = JSObjectCopyPropertyNames(m_context, js_object); - size_t count = JSPropertyNameArrayGetCount(js_keys); + std::vector js_keys = jsc::Object::get_property_names(m_context, js_object); std::vector keys; std::vector values; - for (size_t i = 0; i < count; i++) { - JSStringRef js_key = JSPropertyNameArrayGetNameAtIndex(js_keys, i); - JSValueRef js_value = RJSValidatedPropertyValue(m_context, js_object, js_key); + for (auto &js_key : js_keys) { + JSValueRef js_value = jsc::Object::get_property(m_context, js_object, js_key); - keys.push_back(RJSStringForJSString(js_key)); - values.push_back(serialize_json_value(js_value)); + keys.push_back(js_key); + values.push_back(js_value); } - JSPropertyNameArrayRelease(js_keys); - return { {"type", RealmObjectTypesDictionary}, {"keys", keys}, @@ -312,7 +292,7 @@ json RPCServer::serialize_json_value(JSValueRef value) { json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema) { std::vector properties; - for (realm::Property prop : object_schema.properties) { + for (auto &prop : object_schema.properties) { properties.push_back(prop.name); } @@ -322,8 +302,7 @@ json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema }; } -JSValueRef RPCServer::deserialize_json_value(const json dict) -{ +JSValueRef RPCServer::deserialize_json_value(const json dict) { json oid = dict["id"]; if (oid.is_number()) { return m_objects[oid.get()]; @@ -331,28 +310,24 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) json value = dict["value"]; json type = dict["type"]; + if (type.is_string()) { std::string type_string = type.get(); + if (type_string == RealmObjectTypesFunction) { // FIXME: Make this actually call the function by its id once we need it to. - JSStringRef js_body = JSStringCreateWithUTF8CString(""); - JSObjectRef js_function = JSObjectMakeFunction(m_context, NULL, 0, NULL, js_body, NULL, 1, NULL); - JSStringRelease(js_body); - - return js_function; + return JSObjectMakeFunction(m_context, NULL, 0, NULL, jsc::String(""), NULL, 1, NULL); } else if (type_string == RealmObjectTypesDictionary) { - JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL); + JSObjectRef js_object = jsc::Object::create_empty(m_context); json keys = dict["keys"]; json values = dict["values"]; size_t count = keys.size(); for (size_t i = 0; i < count; i++) { - JSStringRef js_key = RJSStringForString(keys.at(i)); + std::string js_key = keys.at(i); JSValueRef js_value = deserialize_json_value(values.at(i)); - - JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL); - JSStringRelease(js_key); + jsc::Object::set_property(m_context, js_object, js_key, js_value); } return js_object; @@ -362,35 +337,28 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) if (!base64_decode(value.get(), &bytes)) { throw std::runtime_error("Failed to decode base64 encoded data"); } - return RJSAccessor::from_binary(m_context, realm::BinaryData(bytes)); + return Accessor::from_binary(m_context, realm::BinaryData(bytes)); } else if (type_string == RealmObjectTypesDate) { - JSValueRef exception = NULL; - JSValueRef time = JSValueMakeNumber(m_context, value.get()); - JSObjectRef date = JSObjectMakeDate(m_context, 1, &time, &exception); - - if (exception) { - throw RJSException(m_context, exception); - } - return date; + return jsc::Object::create_date(m_context, value.get()); } else if (type_string == RealmObjectTypesUndefined) { - return JSValueMakeUndefined(m_context); + return jsc::Value::from_undefined(m_context); } assert(0); } if (value.is_null()) { - return JSValueMakeNull(m_context); + return jsc::Value::from_null(m_context); } else if (value.is_boolean()) { - return JSValueMakeBoolean(m_context, value.get()); + return jsc::Value::from_boolean(m_context, value.get()); } else if (value.is_number()) { - return JSValueMakeNumber(m_context, value.get()); + return jsc::Value::from_number(m_context, value.get()); } else if (value.is_string()) { - return RJSValueForString(m_context, value.get()); + return jsc::Value::from_string(m_context, value.get()); } else if (value.is_array()) { size_t count = value.size(); @@ -400,7 +368,7 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) js_values[i] = deserialize_json_value(value.at(i)); } - return JSObjectMakeArray(m_context, count, js_values, NULL); + return jsc::Object::create_array(m_context, (uint32_t)count, js_values); } assert(0); } diff --git a/src/rpc.hpp b/src/rpc.hpp index f0580f4b..ae015c5c 100644 --- a/src/rpc.hpp +++ b/src/rpc.hpp @@ -19,13 +19,13 @@ #pragma once #include "json.hpp" -#include +#include "jsc_types.hpp" namespace realm { - class ObjectSchema; -} -namespace realm_js { +class ObjectSchema; + +namespace rpc { using json = nlohmann::json; using RPCObjectID = u_int64_t; @@ -48,8 +48,8 @@ class RPCServer { json serialize_json_value(JSValueRef value); JSValueRef deserialize_json_value(const json dict); - json serialize_object_schema(const realm::ObjectSchema &objectSchema); + json serialize_object_schema(const ObjectSchema &objectSchema); }; -} - +} // rpc +} // realm From 0b2a75bdc3eaac3f19f8e4b9eb8020e38377b6d5 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 14 Apr 2016 10:54:43 -0700 Subject: [PATCH 16/48] use different naming convention for extracted template types --- src/js_list.hpp | 56 +++++----- src/js_object.hpp | 32 +++--- src/js_object_accessor.hpp | 72 ++++++------- src/js_realm.hpp | 144 ++++++++++++------------- src/js_results.hpp | 60 +++++------ src/js_schema.hpp | 56 +++++----- src/js_types.hpp | 211 ++++++++++++++++++------------------- 7 files changed, 313 insertions(+), 318 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index 7c2e82a6..ed5f17d7 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -34,27 +34,27 @@ namespace js { template struct List { - using ContextType = typename T::Context; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TContext = typename T::Context; + using TObject = typename T::Object; + using TValue = typename T::Value; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; - static ObjectType create(ContextType, realm::List &); + static TObject create(TContext, realm::List &); - static void GetLength(ContextType, ObjectType, ReturnValue &); - static void GetIndex(ContextType, ObjectType, uint32_t, ReturnValue &); - static bool SetIndex(ContextType, ObjectType, uint32_t, ValueType); + static void GetLength(TContext, TObject, ReturnValue &); + static void GetIndex(TContext, TObject, uint32_t, ReturnValue &); + static bool SetIndex(TContext, TObject, uint32_t, TValue); - static void Push(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Pop(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Unshift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Shift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Splice(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void StaticResults(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Push(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Pop(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Unshift(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Shift(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Splice(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void StaticResults(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); }; template @@ -82,18 +82,18 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object List::create(ContextType ctx, realm::List &list) { +typename T::Object List::create(TContext ctx, realm::List &list) { return create_object(ctx, new realm::List(list)); } template -void List::GetLength(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void List::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { auto list = get_internal(object); return_value.set((uint32_t)list->size()); } template -void List::GetIndex(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { +void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { auto list = get_internal(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); @@ -101,14 +101,14 @@ void List::GetIndex(ContextType ctx, ObjectType object, uint32_t index, Retur } template -bool List::SetIndex(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { +bool List::SetIndex(TContext ctx, TObject object, uint32_t index, TValue value) { auto list = get_internal(object); list->set(ctx, value, index); return true; } template -void List::Push(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Push(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal(this_object); @@ -120,7 +120,7 @@ void List::Push(ContextType ctx, ObjectType this_object, size_t argc, const V } template -void List::Pop(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Pop(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal(this_object); @@ -139,7 +139,7 @@ void List::Pop(ContextType ctx, ObjectType this_object, size_t argc, const Va } template -void List::Unshift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Unshift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal(this_object); @@ -151,7 +151,7 @@ void List::Unshift(ContextType ctx, ObjectType this_object, size_t argc, cons } template -void List::Shift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Shift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal(this_object); @@ -168,7 +168,7 @@ void List::Shift(ContextType ctx, ObjectType this_object, size_t argc, const } template -void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal(this_object); @@ -187,7 +187,7 @@ void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const remove = std::min(remove, size - index); } - std::vector removed_objects; + std::vector removed_objects; removed_objects.reserve(remove); for (size_t i = 0; i < remove; i++) { @@ -204,7 +204,7 @@ void List::Splice(ContextType ctx, ObjectType this_object, size_t argc, const } template -void List::StaticResults(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal(this_object); @@ -212,7 +212,7 @@ void List::StaticResults(ContextType ctx, ObjectType this_object, size_t argc } template -void List::Filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal(this_object); @@ -220,7 +220,7 @@ void List::Filtered(ContextType ctx, ObjectType this_object, size_t argc, con } template -void List::Sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void List::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto list = get_internal(this_object); diff --git a/src/js_object.hpp b/src/js_object.hpp index 60e9e5d7..7e1316e6 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -30,21 +30,21 @@ namespace js { template struct RealmObject { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; using String = String; using Value = Value; using Object = Object; using Function = Function; using ReturnValue = ReturnValue; - static ObjectType create(ContextType, realm::Object &); + static TObject create(TContext, realm::Object &); - static void GetProperty(ContextType, ObjectType, const String &, ReturnValue &); - static bool SetProperty(ContextType, ObjectType, const String &, ValueType); - static std::vector GetPropertyNames(ContextType, ObjectType); + static void GetProperty(TContext, TObject, const String &, ReturnValue &); + static bool SetProperty(TContext, TObject, const String &, TValue); + static std::vector GetPropertyNames(TContext, TObject); }; template @@ -61,7 +61,7 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object RealmObject::create(ContextType ctx, realm::Object &realm_object) { +typename T::Object RealmObject::create(TContext ctx, realm::Object &realm_object) { static String s_prototype = "prototype"; auto delegate = get_delegate(realm_object.realm().get()); @@ -72,11 +72,11 @@ typename T::Object RealmObject::create(ContextType ctx, realm::Object &realm_ return object; } - FunctionType constructor = delegate->m_constructors.at(name); - ObjectType prototype = Object::validated_get_object(ctx, constructor, s_prototype); + TFunction constructor = delegate->m_constructors.at(name); + TObject prototype = Object::validated_get_object(ctx, constructor, s_prototype); Object::set_prototype(ctx, object, prototype); - ValueType result = Function::call(ctx, constructor, object, 0, NULL); + TValue result = Function::call(ctx, constructor, object, 0, NULL); if (result != object && !Value::is_null(ctx, result) && !Value::is_undefined(ctx, result)) { throw std::runtime_error("Realm object constructor must not return another value"); } @@ -85,10 +85,10 @@ typename T::Object RealmObject::create(ContextType ctx, realm::Object &realm_ } template -void RealmObject::GetProperty(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { +void RealmObject::GetProperty(TContext ctx, TObject object, const String &property, ReturnValue &return_value) { try { auto realm_object = get_internal(object); - auto result = realm_object->template get_property_value(ctx, property); + auto result = realm_object->template get_property_value(ctx, property); return_value.set(result); } catch (InvalidPropertyException &ex) { // getters for nonexistent properties in JS should always return undefined @@ -96,14 +96,14 @@ void RealmObject::GetProperty(ContextType ctx, ObjectType object, const Strin } template -bool RealmObject::SetProperty(ContextType ctx, ObjectType object, const String &property, ValueType value) { +bool RealmObject::SetProperty(TContext ctx, TObject object, const String &property, TValue value) { auto realm_object = get_internal(object); realm_object->set_property_value(ctx, property, value, true); return true; } template -std::vector> RealmObject::GetPropertyNames(ContextType ctx, ObjectType object) { +std::vector> RealmObject::GetPropertyNames(TContext ctx, TObject object) { auto realm_object = get_internal(object); auto &properties = realm_object->get_object_schema().properties; diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index f9788acd..a1316fa3 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -27,82 +27,82 @@ namespace js { template class NativeAccessor { - using ContextType = typename T::Context; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TContext = typename T::Context; + using TObject = typename T::Object; + using TValue = typename T::Value; using Object = Object; using Value = Value; public: - static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { - ObjectType object = Value::validated_to_object(ctx, dict); + static bool dict_has_value_for_key(TContext ctx, TValue dict, const std::string &prop_name) { + TObject object = Value::validated_to_object(ctx, dict); return Object::has_property(ctx, object, prop_name); } - static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { - ObjectType object = Value::validated_to_object(ctx, dict); + static TValue dict_value_for_key(TContext ctx, TValue dict, const std::string &prop_name) { + TObject object = Value::validated_to_object(ctx, dict); return Object::get_property(ctx, object, prop_name); } - static bool has_default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + static bool has_default_value_for_property(TContext ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; return defaults.count(prop_name) != 0; } - static ValueType default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + static TValue default_value_for_property(TContext ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; return defaults.at(prop_name); } // These must be implemented for each JS engine. - static std::string to_binary(ContextType, ValueType &); - static ValueType from_binary(ContextType, BinaryData); + static std::string to_binary(TContext, TValue &); + static TValue from_binary(TContext, BinaryData); - static bool to_bool(ContextType ctx, ValueType &value) { + static bool to_bool(TContext ctx, TValue &value) { return Value::validated_to_boolean(ctx, value, "Property"); } - static ValueType from_bool(ContextType ctx, bool boolean) { + static TValue from_bool(TContext ctx, bool boolean) { return Value::from_boolean(ctx, boolean); } - static long long to_long(ContextType ctx, ValueType &value) { + static long long to_long(TContext ctx, TValue &value) { return Value::validated_to_number(ctx, value, "Property"); } - static ValueType from_long(ContextType ctx, long long number) { + static TValue from_long(TContext ctx, long long number) { return Value::from_number(ctx, number); } - static float to_float(ContextType ctx, ValueType &value) { + static float to_float(TContext ctx, TValue &value) { return Value::validated_to_number(ctx, value, "Property"); } - static ValueType from_float(ContextType ctx, float number) { + static TValue from_float(TContext ctx, float number) { return Value::from_number(ctx, number); } - static double to_double(ContextType ctx, ValueType &value) { + static double to_double(TContext ctx, TValue &value) { return Value::validated_to_number(ctx, value, "Property"); } - static ValueType from_double(ContextType ctx, double number) { + static TValue from_double(TContext ctx, double number) { return Value::from_number(ctx, number); } - static std::string to_string(ContextType ctx, ValueType &value) { + static std::string to_string(TContext ctx, TValue &value) { return Value::validated_to_string(ctx, value, "Property"); } - static ValueType from_string(ContextType ctx, StringData string) { + static TValue from_string(TContext ctx, StringData string) { return Value::from_string(ctx, string.data()); } - static DateTime to_datetime(ContextType ctx, ValueType &value) { - ObjectType date = Value::validated_to_date(ctx, value, "Property"); + static DateTime to_datetime(TContext ctx, TValue &value) { + TObject date = Value::validated_to_date(ctx, value, "Property"); return DateTime(Value::to_number(ctx, date)); } - static ValueType from_datetime(ContextType ctx, DateTime dt) { + static TValue from_datetime(TContext ctx, DateTime dt) { return Object::create_date(ctx, dt.get_datetime()); } - static bool is_null(ContextType ctx, ValueType &value) { + static bool is_null(TContext ctx, TValue &value) { return Value::is_null(ctx, value) || Value::is_undefined(ctx, value); } - static ValueType null_value(ContextType ctx) { + static TValue null_value(TContext ctx) { return Value::from_null(ctx); } - static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &value, const std::string &type, bool try_update) { - ObjectType object = Value::validated_to_object(ctx, value); + static size_t to_object_index(TContext ctx, SharedRealm realm, TValue &value, const std::string &type, bool try_update) { + TObject object = Value::validated_to_object(ctx, value); if (Object::template is_instance(ctx, object)) { return get_internal(object)->row().get_index(); } @@ -112,31 +112,31 @@ class NativeAccessor { object = Schema::dict_for_property_array(ctx, *object_schema, object); } - auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); + auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); return child.row().get_index(); } - static size_t to_existing_object_index(ContextType ctx, ValueType &value) { - ObjectType object = Value::validated_to_object(ctx, value); + static size_t to_existing_object_index(TContext ctx, TValue &value) { + TObject object = Value::validated_to_object(ctx, value); if (Object::template is_instance(ctx, object)) { return get_internal(object)->row().get_index(); } throw std::runtime_error("object is not a Realm Object"); } - static ValueType from_object(ContextType ctx, realm::Object realm_object) { + static TValue from_object(TContext ctx, realm::Object realm_object) { return RealmObject::create(ctx, realm_object); } - static size_t list_size(ContextType ctx, ValueType &value) { + static size_t list_size(TContext ctx, TValue &value) { return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value)); } - static ValueType list_value_at_index(ContextType ctx, ValueType &value, size_t index) { + static TValue list_value_at_index(TContext ctx, TValue &value, size_t index) { return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index); } - static ValueType from_list(ContextType ctx, realm::List list) { + static TValue from_list(TContext ctx, realm::List list) { return List::create(ctx, list); } - static Mixed to_mixed(ContextType ctx, ValueType &val) { + static Mixed to_mixed(TContext ctx, TValue &val) { throw std::runtime_error("'Any' type is unsupported"); } }; diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 1c6ddc29..e4784ea3 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -40,10 +40,10 @@ namespace js { template class RealmDelegate : public BindingContext { public: - using GlobalContextType = typename T::GlobalContext; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TGlobalContext = typename T::GlobalContext; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; using Value = Value; using ObjectDefaultsMap = typename Schema::ObjectDefaultsMap; @@ -57,17 +57,17 @@ class RealmDelegate : public BindingContext { } virtual void will_change(std::vector const& observers, std::vector const& invalidated) {} - RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {} + RealmDelegate(std::weak_ptr realm, TGlobalContext ctx) : m_context(ctx), m_realm(realm) {} ~RealmDelegate() { remove_all_notifications(); } - void add_notification(FunctionType notification) { - m_notifications.insert(Protected(m_context, notification)); + void add_notification(TFunction notification) { + m_notifications.insert(Protected(m_context, notification)); } - void remove_notification(FunctionType notification) { - m_notifications.erase(Protected(m_context, notification)); + void remove_notification(TFunction notification) { + m_notifications.erase(Protected(m_context, notification)); } void remove_all_notifications() { m_notifications.clear(); @@ -77,8 +77,8 @@ class RealmDelegate : public BindingContext { ConstructorMap m_constructors; private: - Protected m_context; - std::set> m_notifications; + Protected m_context; + std::set> m_notifications; std::weak_ptr m_realm; void notify(const char *notification_name) { @@ -87,8 +87,8 @@ class RealmDelegate : public BindingContext { throw std::runtime_error("Realm no longer exists"); } - ObjectType realm_object = create_object(m_context, new SharedRealm(realm)); - ValueType arguments[2]; + TObject realm_object = create_object(m_context, new SharedRealm(realm)); + TValue arguments[2]; arguments[0] = realm_object; arguments[1] = Value::from_string(m_context, notification_name); @@ -103,47 +103,47 @@ void set_default_path(std::string path); void clear_test_state(); template -class Realm : public BindingContext { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; +class Realm { + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; using String = String; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; - using NativeAccessor = realm::NativeAccessor; + using NativeAccessor = realm::NativeAccessor; public: // member methods - static void Objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Delete(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void DeleteAll(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Write(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void AddListener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void RemoveListener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void RemoveAllListeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Close(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Objects(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Create(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Delete(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void DeleteAll(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Write(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void AddListener(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void RemoveListener(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void RemoveAllListeners(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Close(TContext, TObject, size_t, const TValue[], ReturnValue &); // properties - static void GetPath(ContextType, ObjectType, ReturnValue &); - static void GetSchemaVersion(ContextType, ObjectType, ReturnValue &); + static void GetPath(TContext, TObject, ReturnValue &); + static void GetSchemaVersion(TContext, TObject, ReturnValue &); // constructor methods - static void Constructor(ContextType, ObjectType, size_t, const ValueType[]); - static void SchemaVersion(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void ClearTestState(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void Constructor(TContext, TObject, size_t, const TValue[]); + static void SchemaVersion(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void ClearTestState(TContext, TObject, size_t, const TValue[], ReturnValue &); // static properties - static void GetDefaultPath(ContextType, ObjectType, ReturnValue &); - static void SetDefaultPath(ContextType, ObjectType, ValueType value); + static void GetDefaultPath(TContext, TObject, ReturnValue &); + static void SetDefaultPath(TContext, TObject, TValue value); - static ObjectType create_constructor(ContextType ctx) { - ObjectType realm_constructor = ObjectWrap::create_constructor(ctx); - ObjectType collection_constructor = ObjectWrap::create_constructor(ctx); - ObjectType list_constructor = ObjectWrap::create_constructor(ctx); - ObjectType results_constructor = ObjectWrap::create_constructor(ctx); + static TObject create_constructor(TContext ctx) { + TObject realm_constructor = ObjectWrap::create_constructor(ctx); + TObject collection_constructor = ObjectWrap::create_constructor(ctx); + TObject list_constructor = ObjectWrap::create_constructor(ctx); + TObject results_constructor = ObjectWrap::create_constructor(ctx); PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); @@ -153,7 +153,7 @@ class Realm : public BindingContext { return realm_constructor; } - static std::string validated_notification_name(ContextType ctx, const ValueType &value) { + static std::string validated_notification_name(TContext ctx, const TValue &value) { std::string name = Value::validated_to_string(ctx, value, "notification name"); if (name != "change") { throw std::runtime_error("Only the 'change' notification name is supported."); @@ -162,13 +162,13 @@ class Realm : public BindingContext { } // converts constructor object or type name to type name - static std::string validated_object_type_for_value(SharedRealm &realm, ContextType ctx, const ValueType &value) { + static std::string validated_object_type_for_value(SharedRealm &realm, TContext ctx, const TValue &value) { if (Value::is_constructor(ctx, value)) { - FunctionType constructor = Value::to_constructor(ctx, value); + TFunction constructor = Value::to_constructor(ctx, value); auto delegate = get_delegate(realm.get()); for (auto &pair : delegate->m_constructors) { - if (FunctionType(pair.second) == constructor) { + if (TFunction(pair.second) == constructor) { return pair.first; } } @@ -221,7 +221,7 @@ struct ObjectClass : BaseObjectClass { }; template -void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[]) { +void Realm::Constructor(TContext ctx, TObject this_object, size_t argc, const TValue arguments[]) { static const String path_string = "path"; static const String schema_string = "schema"; static const String schema_version_string = "schemaVersion"; @@ -235,14 +235,14 @@ void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, config.path = default_path(); } else if (argc == 1) { - ValueType value = arguments[0]; + TValue value = arguments[0]; if (Value::is_string(ctx, value)) { config.path = Value::validated_to_string(ctx, value, "path"); } else if (Value::is_object(ctx, value)) { - ObjectType object = Value::validated_to_object(ctx, value); + TObject object = Value::validated_to_object(ctx, value); - ValueType pathValue = Object::get_property(ctx, object, path_string); + TValue pathValue = Object::get_property(ctx, object, path_string); if (!Value::is_undefined(ctx, pathValue)) { config.path = Value::validated_to_string(ctx, pathValue, "path"); } @@ -250,13 +250,13 @@ void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, config.path = js::default_path(); } - ValueType schemaValue = Object::get_property(ctx, object, schema_string); + TValue schemaValue = Object::get_property(ctx, object, schema_string); if (!Value::is_undefined(ctx, schemaValue)) { - ObjectType schemaObject = Value::validated_to_object(ctx, schemaValue, "schema"); + TObject schemaObject = Value::validated_to_object(ctx, schemaValue, "schema"); config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, schemaObject, defaults, constructors))); } - ValueType versionValue = Object::get_property(ctx, object, schema_version_string); + TValue versionValue = Object::get_property(ctx, object, schema_version_string); if (!Value::is_undefined(ctx, versionValue)) { config.schema_version = Value::validated_to_number(ctx, versionValue, "schemaVersion"); } @@ -264,7 +264,7 @@ void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, config.schema_version = 0; } - ValueType encryptionKeyValue = Object::get_property(ctx, object, encryption_key_string); + TValue encryptionKeyValue = Object::get_property(ctx, object, encryption_key_string); if (!Value::is_undefined(ctx, encryptionKeyValue)) { std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue); config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); @@ -292,7 +292,7 @@ void Realm::Constructor(ContextType ctx, ObjectType this_object, size_t argc, } template -void Realm::SchemaVersion(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::SchemaVersion(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); realm::Realm::Config config; @@ -313,35 +313,35 @@ void Realm::SchemaVersion(ContextType ctx, ObjectType this_object, size_t arg } template -void Realm::ClearTestState(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::ClearTestState(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); clear_test_state(); } template -void Realm::GetDefaultPath(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void Realm::GetDefaultPath(TContext ctx, TObject object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); } template -void Realm::SetDefaultPath(ContextType ctx, ObjectType object, ValueType value) { +void Realm::SetDefaultPath(TContext ctx, TObject object, TValue value) { js::set_default_path(Value::validated_to_string(ctx, value, "defaultPath")); } template -void Realm::GetPath(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void Realm::GetPath(TContext ctx, TObject object, ReturnValue &return_value) { std::string path = get_internal(object)->get()->config().path; return_value.set(path); } template -void Realm::GetSchemaVersion(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void Realm::GetSchemaVersion(TContext ctx, TObject object, ReturnValue &return_value) { double version = get_internal(object)->get()->config().schema_version; return_value.set(version); } template -void Realm::Objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::Objects(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal(this_object); @@ -351,7 +351,7 @@ void Realm::Objects(ContextType ctx, ObjectType this_object, size_t argc, con } template -void Realm::Create(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::Create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2, 3); SharedRealm sharedRealm = *get_internal(this_object); @@ -363,7 +363,7 @@ void Realm::Create(ContextType ctx, ObjectType this_object, size_t argc, cons throw std::runtime_error("Object type '" + className + "' not found in schema."); } - ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties"); + TObject object = Value::validated_to_object(ctx, arguments[1], "properties"); if (Value::is_array(ctx, arguments[1])) { object = Schema::dict_for_property_array(ctx, *object_schema, object); } @@ -373,12 +373,12 @@ void Realm::Create(ContextType ctx, ObjectType this_object, size_t argc, cons update = Value::validated_to_boolean(ctx, arguments[2], "update"); } - auto realm_object = realm::Object::create(ctx, sharedRealm, *object_schema, object, update); + auto realm_object = realm::Object::create(ctx, sharedRealm, *object_schema, object, update); return_value.set(RealmObject::create(ctx, realm_object)); } template -void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::Delete(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal(this_object); @@ -386,7 +386,7 @@ void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, cons throw std::runtime_error("Can only delete objects within a transaction."); } - ObjectType arg = Value::validated_to_object(ctx, arguments[0]); + TObject arg = Value::validated_to_object(ctx, arguments[0]); if (Object::template is_instance(ctx, arg)) { auto object = get_internal(arg); @@ -396,7 +396,7 @@ void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, cons else if (Value::is_array(ctx, arg)) { uint32_t length = Object::validated_get_length(ctx, arg); for (uint32_t i = length; i--;) { - ObjectType object = Object::validated_get_object(ctx, arg, i); + TObject object = Object::validated_get_object(ctx, arg, i); if (!Object::template is_instance(ctx, object)) { throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); @@ -421,7 +421,7 @@ void Realm::Delete(ContextType ctx, ObjectType this_object, size_t argc, cons } template -void Realm::DeleteAll(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::DeleteAll(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal(this_object); @@ -436,11 +436,11 @@ void Realm::DeleteAll(ContextType ctx, ObjectType this_object, size_t argc, c } template -void Realm::Write(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::Write(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal(this_object); - FunctionType callback = Value::validated_to_function(ctx, arguments[0]); + TFunction callback = Value::validated_to_function(ctx, arguments[0]); try { realm->begin_transaction(); @@ -456,7 +456,7 @@ void Realm::Write(ContextType ctx, ObjectType this_object, size_t argc, const } template -void Realm::AddListener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::AddListener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -467,7 +467,7 @@ void Realm::AddListener(ContextType ctx, ObjectType this_object, size_t argc, } template -void Realm::RemoveListener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::RemoveListener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -478,7 +478,7 @@ void Realm::RemoveListener(ContextType ctx, ObjectType this_object, size_t ar } template -void Realm::RemoveAllListeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::RemoveAllListeners(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0, 1); if (argc) { validated_notification_name(ctx, arguments[0]); @@ -489,7 +489,7 @@ void Realm::RemoveAllListeners(ContextType ctx, ObjectType this_object, size_ } template -void Realm::Close(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Realm::Close(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal(this_object); diff --git a/src/js_results.hpp b/src/js_results.hpp index 2488e5ce..b8e7822f 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -31,30 +31,30 @@ namespace js { template struct Results { - using ContextType = typename T::Context; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TContext = typename T::Context; + using TObject = typename T::Object; + using TValue = typename T::Value; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; - static ObjectType create(ContextType, const realm::Results &, bool live = true); - static ObjectType create(ContextType, const realm::List &, bool live = true); - static ObjectType create(ContextType, SharedRealm, const std::string &type, bool live = true); - static ObjectType create(ContextType, SharedRealm, const ObjectSchema &, Query, bool live = true); + static TObject create(TContext, const realm::Results &, bool live = true); + static TObject create(TContext, const realm::List &, bool live = true); + static TObject create(TContext, SharedRealm, const std::string &type, bool live = true); + static TObject create(TContext, SharedRealm, const ObjectSchema &, Query, bool live = true); template - static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]); + static TObject create_filtered(TContext, const U &, size_t, const TValue[]); template - static ObjectType create_sorted(ContextType, const U &, size_t, const ValueType[]); + static TObject create_sorted(TContext, const U &, size_t, const TValue[]); - static void GetLength(ContextType, ObjectType, ReturnValue &); - static void GetIndex(ContextType, ObjectType, uint32_t, ReturnValue &); + static void GetLength(TContext, TObject, ReturnValue &); + static void GetIndex(TContext, TObject, uint32_t, ReturnValue &); - static void StaticResults(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void Sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void StaticResults(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void Sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); }; template @@ -77,7 +77,7 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object Results::create(ContextType ctx, const realm::Results &results, bool live) { +typename T::Object Results::create(TContext ctx, const realm::Results &results, bool live) { auto new_results = new realm::Results(results); new_results->set_live(live); @@ -85,12 +85,12 @@ typename T::Object Results::create(ContextType ctx, const realm::Results &res } template -typename T::Object Results::create(ContextType ctx, const realm::List &list, bool live) { +typename T::Object Results::create(TContext ctx, const realm::List &list, bool live) { return create(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live); } template -typename T::Object Results::create(ContextType ctx, SharedRealm realm, const std::string &type, bool live) { +typename T::Object Results::create(TContext ctx, SharedRealm realm, const std::string &type, bool live) { auto table = ObjectStore::table_for_object_type(realm->read_group(), type); auto &schema = realm->config().schema; auto object_schema = schema->find(type); @@ -106,7 +106,7 @@ typename T::Object Results::create(ContextType ctx, SharedRealm realm, const } template -typename T::Object Results::create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { +typename T::Object Results::create(TContext ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { auto results = new realm::Results(realm, object_schema, std::move(query)); results->set_live(live); @@ -115,13 +115,13 @@ typename T::Object Results::create(ContextType ctx, SharedRealm realm, const template template -typename T::Object Results::create_filtered(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { +typename T::Object Results::create_filtered(TContext ctx, const U &collection, size_t argc, const TValue arguments[]) { auto query_string = Value::validated_to_string(ctx, arguments[0], "predicate"); auto query = collection.get_query(); auto const &realm = collection.get_realm(); auto const &object_schema = collection.get_object_schema(); - std::vector args; + std::vector args; args.reserve(argc - 1); for (size_t i = 1; i < argc; i++) { @@ -129,7 +129,7 @@ typename T::Object Results::create_filtered(ContextType ctx, const U &collect } parser::Predicate predicate = parser::parse(query_string); - query_builder::ArgumentConverter converter(ctx, args); + query_builder::ArgumentConverter converter(ctx, args); query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name); return create(ctx, realm, object_schema, std::move(query)); @@ -137,7 +137,7 @@ typename T::Object Results::create_filtered(ContextType ctx, const U &collect template template -typename T::Object Results::create_sorted(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { +typename T::Object Results::create_sorted(TContext ctx, const U &collection, size_t argc, const TValue arguments[]) { auto const &realm = collection.get_realm(); auto const &object_schema = collection.get_object_schema(); std::vector prop_names; @@ -147,7 +147,7 @@ typename T::Object Results::create_sorted(ContextType ctx, const U &collectio if (Value::is_array(ctx, arguments[0])) { validate_argument_count(argc, 1, "Second argument is not allowed if passed an array of sort descriptors"); - ObjectType js_prop_names = Value::validated_to_object(ctx, arguments[0]); + TObject js_prop_names = Value::validated_to_object(ctx, arguments[0]); prop_count = Object::validated_get_length(ctx, js_prop_names); if (!prop_count) { throw std::invalid_argument("Sort descriptor array must not be empty"); @@ -157,10 +157,10 @@ typename T::Object Results::create_sorted(ContextType ctx, const U &collectio ascending.resize(prop_count); for (unsigned int i = 0; i < prop_count; i++) { - ValueType value = Object::validated_get_property(ctx, js_prop_names, i); + TValue value = Object::validated_get_property(ctx, js_prop_names, i); if (Value::is_array(ctx, value)) { - ObjectType array = Value::to_array(ctx, value); + TObject array = Value::to_array(ctx, value); prop_names[i] = Object::validated_get_string(ctx, array, 0); ascending[i] = !Object::validated_get_boolean(ctx, array, 1); } @@ -194,13 +194,13 @@ typename T::Object Results::create_sorted(ContextType ctx, const U &collectio } template -void Results::GetLength(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void Results::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { auto results = get_internal(object); return_value.set((uint32_t)results->size()); } template -void Results::GetIndex(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { +void Results::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { auto results = get_internal(object); auto row = results->get(index); @@ -215,7 +215,7 @@ void Results::GetIndex(ContextType ctx, ObjectType object, uint32_t index, Re } template -void Results::StaticResults(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Results::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto results = get_internal(this_object); @@ -223,7 +223,7 @@ void Results::StaticResults(ContextType ctx, ObjectType this_object, size_t a } template -void Results::Filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Results::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto results = get_internal(this_object); @@ -231,7 +231,7 @@ void Results::Filtered(ContextType ctx, ObjectType this_object, size_t argc, } template -void Results::Sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { +void Results::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto results = get_internal(this_object); diff --git a/src/js_schema.hpp b/src/js_schema.hpp index c58938fd..9ad69b97 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -28,36 +28,36 @@ namespace js { template struct Schema { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; using String = String; using Object = Object; using Value = Value; - using ObjectDefaults = std::map>; + using ObjectDefaults = std::map>; using ObjectDefaultsMap = std::map; - using ConstructorMap = std::map>; + using ConstructorMap = std::map>; - static ObjectType dict_for_property_array(ContextType, const ObjectSchema &, ObjectType); - static Property parse_property(ContextType, ValueType, std::string, ObjectDefaults &); - static ObjectSchema parse_object_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); - static realm::Schema parse_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); + static TObject dict_for_property_array(TContext, const ObjectSchema &, TObject); + static Property parse_property(TContext, TValue, std::string, ObjectDefaults &); + static ObjectSchema parse_object_schema(TContext, TObject, ObjectDefaultsMap &, ConstructorMap &); + static realm::Schema parse_schema(TContext, TObject, ObjectDefaultsMap &, ConstructorMap &); }; template -typename T::Object Schema::dict_for_property_array(ContextType ctx, const ObjectSchema &object_schema, ObjectType array) { +typename T::Object Schema::dict_for_property_array(TContext ctx, const ObjectSchema &object_schema, TObject array) { size_t count = object_schema.properties.size(); if (count != Object::validated_get_length(ctx, array)) { throw std::runtime_error("Array must contain values for all object properties"); } - ObjectType dict = Object::create_empty(ctx); + TObject dict = Object::create_empty(ctx); for (uint32_t i = 0; i < count; i++) { - ValueType value = Object::get_property(ctx, array, i); + TValue value = Object::get_property(ctx, array, i); Object::set_property(ctx, dict, object_schema.properties[i].name, value); } @@ -65,7 +65,7 @@ typename T::Object Schema::dict_for_property_array(ContextType ctx, const Obj } template -Property Schema::parse_property(ContextType ctx, ValueType attributes, std::string propertyName, ObjectDefaults &objectDefaults) { +Property Schema::parse_property(TContext ctx, TValue attributes, std::string propertyName, ObjectDefaults &objectDefaults) { static const String defaultString = "default"; static const String indexedString = "indexed"; static const String typeString = "type"; @@ -75,14 +75,14 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s Property prop; prop.name = propertyName; - ObjectType propertyObject = {}; + TObject propertyObject = {}; std::string type; if (Value::is_object(ctx, attributes)) { propertyObject = Value::validated_to_object(ctx, attributes); type = Object::validated_get_string(ctx, propertyObject, typeString); - ValueType optionalValue = Object::get_property(ctx, propertyObject, optionalString); + TValue optionalValue = Object::get_property(ctx, propertyObject, optionalString); if (!Value::is_undefined(ctx, optionalValue)) { prop.is_nullable = Value::validated_to_boolean(ctx, optionalValue, "optional"); } @@ -136,12 +136,12 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s } if (Value::is_valid(propertyObject)) { - ValueType defaultValue = Object::get_property(ctx, propertyObject, defaultString); + TValue defaultValue = Object::get_property(ctx, propertyObject, defaultString); if (!Value::is_undefined(ctx, defaultValue)) { - objectDefaults.emplace(prop.name, Protected(ctx, defaultValue)); + objectDefaults.emplace(prop.name, Protected(ctx, defaultValue)); } - ValueType indexedValue = Object::get_property(ctx, propertyObject, indexedString); + TValue indexedValue = Object::get_property(ctx, propertyObject, indexedString); if (!Value::is_undefined(ctx, indexedValue)) { prop.is_indexed = Value::validated_to_boolean(ctx, indexedValue); } @@ -151,13 +151,13 @@ Property Schema::parse_property(ContextType ctx, ValueType attributes, std::s } template -ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { +ObjectSchema Schema::parse_object_schema(TContext ctx, TObject objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { static const String nameString = "name"; static const String primaryString = "primaryKey"; static const String propertiesString = "properties"; static const String schemaString = "schema"; - FunctionType objectConstructor = {}; + TFunction objectConstructor = {}; if (Value::is_constructor(ctx, objectSchemaObject)) { objectConstructor = Value::to_constructor(ctx, objectSchemaObject); objectSchemaObject = Object::validated_get_object(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); @@ -167,11 +167,11 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc ObjectSchema objectSchema; objectSchema.name = Object::validated_get_string(ctx, objectSchemaObject, nameString); - ObjectType propertiesObject = Object::validated_get_object(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); + TObject propertiesObject = Object::validated_get_object(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); if (Value::is_array(ctx, propertiesObject)) { uint32_t length = Object::validated_get_length(ctx, propertiesObject); for (uint32_t i = 0; i < length; i++) { - ObjectType propertyObject = Object::validated_get_object(ctx, propertiesObject, i); + TObject propertyObject = Object::validated_get_object(ctx, propertiesObject, i); std::string propertyName = Object::validated_get_string(ctx, propertyObject, nameString); objectSchema.properties.emplace_back(parse_property(ctx, propertyObject, propertyName, objectDefaults)); } @@ -179,12 +179,12 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc else { auto propertyNames = Object::get_property_names(ctx, propertiesObject); for (auto &propertyName : propertyNames) { - ValueType propertyValue = Object::get_property(ctx, propertiesObject, propertyName); + TValue propertyValue = Object::get_property(ctx, propertiesObject, propertyName); objectSchema.properties.emplace_back(parse_property(ctx, propertyValue, propertyName, objectDefaults)); } } - ValueType primaryValue = Object::get_property(ctx, objectSchemaObject, primaryString); + TValue primaryValue = Object::get_property(ctx, objectSchemaObject, primaryString); if (!Value::is_undefined(ctx, primaryValue)) { objectSchema.primary_key = Value::validated_to_string(ctx, primaryValue); Property *property = objectSchema.primary_key_property(); @@ -196,7 +196,7 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc // Store prototype so that objects of this type will have their prototype set to this prototype object. if (Value::is_valid(objectConstructor)) { - constructors.emplace(objectSchema.name, Protected(ctx, objectConstructor)); + constructors.emplace(objectSchema.name, Protected(ctx, objectConstructor)); } defaults.emplace(objectSchema.name, std::move(objectDefaults)); @@ -205,12 +205,12 @@ ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType objectSc } template -realm::Schema Schema::parse_schema(ContextType ctx, ObjectType jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { +realm::Schema Schema::parse_schema(TContext ctx, TObject jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { std::vector schema; uint32_t length = Object::validated_get_length(ctx, jsonObject); for (uint32_t i = 0; i < length; i++) { - ObjectType jsonObjectSchema = Object::validated_get_object(ctx, jsonObject, i); + TObject jsonObjectSchema = Object::validated_get_object(ctx, jsonObject, i); ObjectSchema objectSchema = parse_object_schema(ctx, jsonObjectSchema, defaults, constructors); schema.emplace_back(std::move(objectSchema)); } diff --git a/src/js_types.hpp b/src/js_types.hpp index 5791b736..cb591612 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -43,7 +43,7 @@ enum PropertyAttributes { }; template -class String { +struct String { using StringType = typename T::String; public: @@ -57,52 +57,50 @@ class String { }; template -class Context { - using ContextType = typename T::Context; - using GlobalContextType = typename T::GlobalContext; +struct Context { + using TContext = typename T::Context; + using GlobalTContext = typename T::GlobalContext; - public: - static GlobalContextType get_global_context(ContextType); + static GlobalTContext get_global_context(TContext); }; template -class Value { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; +struct Value { + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; - public: - static bool is_array(ContextType, const ValueType &); - static bool is_array_buffer(ContextType, const ValueType &); - static bool is_boolean(ContextType, const ValueType &); - static bool is_constructor(ContextType, const ValueType &); - static bool is_date(ContextType, const ValueType &); - static bool is_function(ContextType, const ValueType &); - static bool is_null(ContextType, const ValueType &); - static bool is_number(ContextType, const ValueType &); - static bool is_object(ContextType, const ValueType &); - static bool is_string(ContextType, const ValueType &); - static bool is_undefined(ContextType, const ValueType &); - static bool is_valid(const ValueType &); + static bool is_array(TContext, const TValue &); + static bool is_array_buffer(TContext, const TValue &); + static bool is_boolean(TContext, const TValue &); + static bool is_constructor(TContext, const TValue &); + static bool is_date(TContext, const TValue &); + static bool is_function(TContext, const TValue &); + static bool is_null(TContext, const TValue &); + static bool is_number(TContext, const TValue &); + static bool is_object(TContext, const TValue &); + static bool is_string(TContext, const TValue &); + static bool is_undefined(TContext, const TValue &); + static bool is_valid(const TValue &); - static ValueType from_boolean(ContextType, bool); - static ValueType from_null(ContextType); - static ValueType from_number(ContextType, double); - static ValueType from_string(ContextType, const String &); - static ValueType from_undefined(ContextType); + static TValue from_boolean(TContext, bool); + static TValue from_null(TContext); + static TValue from_number(TContext, double); + static TValue from_string(TContext, const String &); + static TValue from_undefined(TContext); - static ObjectType to_array(ContextType, const ValueType &); - static bool to_boolean(ContextType, const ValueType &); - static FunctionType to_constructor(ContextType, const ValueType &); - static ObjectType to_date(ContextType, const ValueType &); - static FunctionType to_function(ContextType, const ValueType &); - static double to_number(ContextType, const ValueType &); - static ObjectType to_object(ContextType, const ValueType &); - static String to_string(ContextType, const ValueType &); + static TObject to_array(TContext, const TValue &); + static bool to_boolean(TContext, const TValue &); + static TFunction to_constructor(TContext, const TValue &); + static TObject to_date(TContext, const TValue &); + static TFunction to_function(TContext, const TValue &); + static double to_number(TContext, const TValue &); + static TObject to_object(TContext, const TValue &); + static String to_string(TContext, const TValue &); #define VALIDATED(return_t, type) \ - static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \ + static return_t validated_to_##type(TContext ctx, const TValue &value, const char *name = nullptr) { \ if (!is_##type(ctx, value)) { \ std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \ throw std::invalid_argument(prefix + " must be: " #type); \ @@ -110,71 +108,70 @@ class Value { return to_##type(ctx, value); \ } - VALIDATED(ObjectType, array) + VALIDATED(TObject, array) VALIDATED(bool, boolean) - VALIDATED(FunctionType, constructor) - VALIDATED(ObjectType, date) - VALIDATED(FunctionType, function) + VALIDATED(TFunction, constructor) + VALIDATED(TObject, date) + VALIDATED(TFunction, function) VALIDATED(double, number) - VALIDATED(ObjectType, object) + VALIDATED(TObject, object) VALIDATED(String, string) #undef VALIDATED }; template -class Function { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; +struct Function { + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; - public: - static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]); - static ValueType call(ContextType ctx, const FunctionType &function, const ObjectType &this_object, const std::vector &arguments) { + static TValue call(TContext, const TFunction &, const TObject &, size_t, const TValue[]); + static TValue call(TContext ctx, const TFunction &function, const TObject &this_object, const std::vector &arguments) { return call(ctx, function, this_object, arguments.size(), arguments.data()); } - static ObjectType construct(ContextType, const FunctionType &, size_t, const ValueType[]); - static ValueType construct(ContextType ctx, const FunctionType &function, const std::vector &arguments) { + static TObject construct(TContext, const TFunction &, size_t, const TValue[]); + static TValue construct(TContext ctx, const TFunction &function, const std::vector &arguments) { return construct(ctx, function, arguments.size(), arguments.data()); } }; template -class Object { - using ContextType = typename T::Context; - using FunctionType = typename T::Function; - using ObjectType = typename T::Object; - using ValueType = typename T::Value; +struct Object { + using TContext = typename T::Context; + using TFunction = typename T::Function; + using TObject = typename T::Object; + using TValue = typename T::Value; public: - static ValueType get_prototype(ContextType, const ObjectType &); - static void set_prototype(ContextType, const ObjectType &, const ValueType &); + static TValue get_prototype(TContext, const TObject &); + static void set_prototype(TContext, const TObject &, const TValue &); - static bool has_property(ContextType, const ObjectType &, const String &); - static bool has_property(ContextType, const ObjectType &, uint32_t); - static ValueType get_property(ContextType, const ObjectType &, const String &); - static ValueType get_property(ContextType, const ObjectType &, uint32_t); - static void set_property(ContextType, const ObjectType &, const String &, const ValueType &, PropertyAttributes attributes = None); - static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &); - static std::vector> get_property_names(ContextType, const ObjectType &); + static bool has_property(TContext, const TObject &, const String &); + static bool has_property(TContext, const TObject &, uint32_t); + static TValue get_property(TContext, const TObject &, const String &); + static TValue get_property(TContext, const TObject &, uint32_t); + static void set_property(TContext, const TObject &, const String &, const TValue &, PropertyAttributes attributes = None); + static void set_property(TContext, const TObject &, uint32_t, const TValue &); + static std::vector> get_property_names(TContext, const TObject &); template - static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) { + static TValue validated_get_property(TContext ctx, const TObject &object, const P &property, const char *message = nullptr) { if (!has_property(ctx, object, property)) { throw std::out_of_range(message ?: "Object missing expected property: " + util::to_string(property)); } return get_property(ctx, object, property); } - static uint32_t validated_get_length(ContextType ctx, const ObjectType &object) { + static uint32_t validated_get_length(TContext ctx, const TObject &object) { static const String length_string = "length"; return Value::validated_to_number(ctx, get_property(ctx, object, length_string)); } #define VALIDATED(return_t, type) \ - static return_t validated_get_##type(ContextType ctx, const ObjectType &object, const String &key, const char *message = nullptr) { \ + static return_t validated_get_##type(TContext ctx, const TObject &object, const String &key, const char *message = nullptr) { \ try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \ } \ @@ -182,7 +179,7 @@ class Object { throw message ? std::invalid_argument(message) : e; \ } \ } \ - static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \ + static return_t validated_get_##type(TContext ctx, const TObject &object, uint32_t index, const char *message = nullptr) { \ try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, index)); \ } \ @@ -191,78 +188,77 @@ class Object { } \ } - VALIDATED(ObjectType, array) + VALIDATED(TObject, array) VALIDATED(bool, boolean) - VALIDATED(FunctionType, constructor) - VALIDATED(ObjectType, date) - VALIDATED(FunctionType, function) + VALIDATED(TFunction, constructor) + VALIDATED(TObject, date) + VALIDATED(TFunction, function) VALIDATED(double, number) - VALIDATED(ObjectType, object) + VALIDATED(TObject, object) VALIDATED(String, string) #undef VALIDATED - static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, uint32_t argc, const ValueType arguments[]) { - FunctionType method = validated_get_function(ctx, object, name); + static TValue call_method(TContext ctx, const TObject &object, const String &name, uint32_t argc, const TValue arguments[]) { + TFunction method = validated_get_function(ctx, object, name); return Function::call(ctx, method, object, argc, arguments); } - static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, const std::vector &arguments) { + static TValue call_method(TContext ctx, const TObject &object, const String &name, const std::vector &arguments) { return call_method(ctx, object, name, (uint32_t)arguments.size(), arguments.data()); } - static ObjectType create_empty(ContextType); - static ObjectType create_array(ContextType, uint32_t, const ValueType[]); + static TObject create_empty(TContext); + static TObject create_array(TContext, uint32_t, const TValue[]); - static ObjectType create_array(ContextType ctx, const std::vector &values) { + static TObject create_array(TContext ctx, const std::vector &values) { return create_array(ctx, (uint32_t)values.size(), values.data()); } - static ObjectType create_array(ContextType ctx) { + static TObject create_array(TContext ctx) { return create_array(ctx, 0, nullptr); } - static ObjectType create_date(ContextType, double); + static TObject create_date(TContext, double); template - static ObjectType create(ContextType, U*); + static TObject create(TContext, U*); template - static bool is_instance(ContextType, const ObjectType &); + static bool is_instance(TContext, const TObject &); template - static U* get_internal(const ObjectType &); + static U* get_internal(const TObject &); template - static void set_internal(const ObjectType &, U*); + static void set_internal(const TObject &, U*); }; -template +template class Protected { - operator T() const; - bool operator==(const T &) const; - bool operator!=(const T &) const; - bool operator==(const Protected &) const; - bool operator!=(const Protected &) const; - bool operator<(const Protected &) const; + operator TValue() const; + bool operator==(const TValue &) const; + bool operator!=(const TValue &) const; + bool operator==(const Protected &) const; + bool operator!=(const Protected &) const; + bool operator<(const Protected &) const; }; template -class Exception : public std::runtime_error { - using ContextType = typename T::Context; - using ValueType = typename T::Value; +struct Exception : public std::runtime_error { + using TContext = typename T::Context; + using TValue = typename T::Value; - const Protected m_value; + const Protected m_value; - public: - Exception(ContextType ctx, const ValueType &val) + Exception(TContext ctx, const TValue &val) : std::runtime_error(std::string(Value::to_string(ctx, val))), m_value(ctx, val) {} - operator ValueType() const { + operator TValue() const { return m_value; } - static ValueType value(ContextType ctx, const std::string &message); + static TValue value(TContext ctx, const std::string &message); - static ValueType value(ContextType ctx, const std::exception &exp) { + static TValue value(TContext ctx, const std::exception &exp) { if (const Exception *js_exp = dynamic_cast *>(&exp)) { return *js_exp; } @@ -271,11 +267,10 @@ class Exception : public std::runtime_error { }; template -class ReturnValue { - using ValueType = typename T::Value; +struct ReturnValue { + using TValue = typename T::Value; - public: - void set(const ValueType &); + void set(const TValue &); void set(const std::string &); void set(bool); void set(double); From 84559316d8ade47a00e800bce4c0c002f4faf8e0 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 14 Apr 2016 11:06:17 -0700 Subject: [PATCH 17/48] compact property getter/setter types --- src/js_class.hpp | 31 ++++++++++--------------------- src/js_object_accessor.hpp | 3 +-- src/jsc/jsc_class.hpp | 24 ++++++++++-------------- src/node/node_class.hpp | 23 +++++++++-------------- 4 files changed, 30 insertions(+), 51 deletions(-) diff --git a/src/js_class.hpp b/src/js_class.hpp index 046f23db..2a80e927 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -33,41 +33,30 @@ using ConstructorType = void(typename T::Context, typename T::Object, size_t, co template using MethodType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[], ReturnValue &); -template -using PropertyGetterType = void(typename T::Context, typename T::Object, ReturnValue &); - -template -using PropertySetterType = void(typename T::Context, typename T::Object, typename T::Value); - -template -using IndexPropertyGetterType = void(typename T::Context, typename T::Object, uint32_t, ReturnValue &); - -template -using IndexPropertySetterType = bool(typename T::Context, typename T::Object, uint32_t, typename T::Value); - -template -using StringPropertyGetterType = void(typename T::Context, typename T::Object, const String &, ReturnValue &); - -template -using StringPropertySetterType = bool(typename T::Context, typename T::Object, const String &, typename T::Value); - -template -using StringPropertyEnumeratorType = std::vector>(typename T::Context, typename T::Object); - template struct PropertyType { + using GetterType = void(typename T::Context, typename T::Object, ReturnValue &); + using SetterType = void(typename T::Context, typename T::Object, typename T::Value); + typename T::PropertyGetterCallback getter; typename T::PropertySetterCallback setter; }; template struct IndexPropertyType { + using GetterType = void(typename T::Context, typename T::Object, uint32_t, ReturnValue &); + using SetterType = bool(typename T::Context, typename T::Object, uint32_t, typename T::Value); + typename T::IndexPropertyGetterCallback getter; typename T::IndexPropertySetterCallback setter; }; template struct StringPropertyType { + using GetterType = void(typename T::Context, typename T::Object, const String &, ReturnValue &); + using SetterType = bool(typename T::Context, typename T::Object, const String &, typename T::Value); + using EnumeratorType = std::vector>(typename T::Context, typename T::Object); + typename T::StringPropertyGetterCallback getter; typename T::StringPropertySetterCallback setter; typename T::StringPropertyEnumeratorCallback enumerator; diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index a1316fa3..6a28f42e 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -26,14 +26,13 @@ namespace realm { namespace js { template -class NativeAccessor { +struct NativeAccessor { using TContext = typename T::Context; using TObject = typename T::Object; using TValue = typename T::Value; using Object = Object; using Value = Value; - public: static bool dict_has_value_for_key(TContext ctx, TValue dict, const std::string &prop_name) { TObject object = Value::validated_to_object(ctx, dict); return Object::has_property(ctx, object, prop_name); diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 8e1b7b66..7d6982c3 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -31,13 +31,9 @@ using ObjectClass = js::ObjectClass; using BaseObjectClass = js::BaseObjectClass; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; -using PropertyGetterType = js::PropertyGetterType; -using PropertySetterType = js::PropertySetterType; -using IndexPropertyGetterType = js::IndexPropertyGetterType; -using IndexPropertySetterType = js::IndexPropertySetterType; -using StringPropertyGetterType = js::StringPropertyGetterType; -using StringPropertySetterType = js::StringPropertySetterType; -using StringPropertyEnumeratorType = js::StringPropertyEnumeratorType; +using PropertyType = js::PropertyType; +using IndexPropertyType = js::IndexPropertyType; +using StringPropertyType = js::StringPropertyType; using MethodMap = js::MethodMap; using PropertyMap = js::PropertyMap; @@ -314,7 +310,7 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, return return_value; } -template +template JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { jsc::ReturnValue return_value(ctx); try { @@ -326,7 +322,7 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSVa return return_value; } -template +template bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { try { F(ctx, object, value); @@ -338,7 +334,7 @@ bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef return false; } -template +template JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef* exception) { jsc::ReturnValue return_value(ctx); try { @@ -354,7 +350,7 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef return return_value; } -template +template bool wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef value, JSValueRef* exception) { try { return F(ctx, object, index, value); @@ -365,7 +361,7 @@ bool wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef value return false; } -template +template JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { jsc::ReturnValue return_value(ctx); try { @@ -377,7 +373,7 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSVa return return_value; } -template +template bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { try { return F(ctx, object, property, value); @@ -388,7 +384,7 @@ bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef return false; } -template +template void wrap(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { auto names = F(ctx, object); for (auto &name : names) { diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 7a19ad68..6f3aeff4 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -29,14 +29,9 @@ using ObjectClass = js::ObjectClass; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; -using PropertyGetterType = js::PropertyGetterType; -using PropertySetterType = js::PropertySetterType; -using IndexPropertyGetterType = js::IndexPropertyGetterType; -using IndexPropertySetterType = js::IndexPropertySetterType; -using StringPropertyGetterType = js::StringPropertyGetterType; -using StringPropertySetterType = js::StringPropertySetterType; -using StringPropertyEnumeratorType = js::StringPropertyEnumeratorType; using PropertyType = js::PropertyType; +using IndexPropertyType = js::IndexPropertyType; +using StringPropertyType = js::StringPropertyType; static inline std::vector> get_arguments(const Nan::FunctionCallbackInfo &info) { int count = info.Length(); @@ -195,7 +190,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { } } -template +Type::template void wrap(v8::Local property, Nan::NAN_GETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); node::ReturnValue return_value(info.GetReturnValue()); @@ -207,7 +202,7 @@ void wrap(v8::Local property, Nan::NAN_GETTER_ARGS_TYPE info) { } } -template +template void wrap(v8::Local property, v8::Local value, Nan::NAN_SETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); try { @@ -218,7 +213,7 @@ void wrap(v8::Local property, v8::Local value, Nan::NAN_S } } -template +template void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); node::ReturnValue return_value(info.GetReturnValue()); @@ -230,7 +225,7 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) { } } -template +template void wrap(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); try { @@ -241,7 +236,7 @@ void wrap(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS } } -template +template void wrap(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); node::ReturnValue return_value(info.GetReturnValue()); @@ -253,7 +248,7 @@ void wrap(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE inf } } -template +template void wrap(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); try { @@ -264,7 +259,7 @@ void wrap(v8::Local property, v8::Local value, Nan::NAN_P } } -template +template void wrap(Nan::NAN_PROPERTY_ENUMERATOR_ARGS_TYPE info) { auto names = F(info.GetIsolate(), info.This()); int count = (int)names.size(); From c817ac7eacefc30ca0ba91c9f4977c3f83aeb0b9 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 14 Apr 2016 11:19:01 -0700 Subject: [PATCH 18/48] create -> create_instance --- src/js_list.hpp | 14 +++++++------- src/js_object.hpp | 4 ++-- src/js_object_accessor.hpp | 4 ++-- src/js_realm.hpp | 4 ++-- src/js_results.hpp | 24 ++++++++++++------------ src/js_types.hpp | 4 ++-- src/jsc/jsc_class.hpp | 4 ++-- src/jsc/jsc_types.hpp | 4 ++-- src/node/node_class.hpp | 2 +- src/node/node_types.hpp | 4 ++-- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index ed5f17d7..167521c5 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -41,7 +41,7 @@ struct List { using Value = Value; using ReturnValue = ReturnValue; - static TObject create(TContext, realm::List &); + static TObject create_instance(TContext, realm::List &); static void GetLength(TContext, TObject, ReturnValue &); static void GetIndex(TContext, TObject, uint32_t, ReturnValue &); @@ -82,7 +82,7 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object List::create(TContext ctx, realm::List &list) { +typename T::Object List::create_instance(TContext ctx, realm::List &list) { return create_object(ctx, new realm::List(list)); } @@ -97,7 +97,7 @@ void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue auto list = get_internal(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); - return_value.set(RealmObject::create(ctx, realm_object)); + return_value.set(RealmObject::create_instance(ctx, realm_object)); } template @@ -133,7 +133,7 @@ void List::Pop(TContext ctx, TObject this_object, size_t argc, const TValue a size_t index = size - 1; auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); - return_value.set(RealmObject::create(ctx, realm_object)); + return_value.set(RealmObject::create_instance(ctx, realm_object)); list->remove(index); } } @@ -162,7 +162,7 @@ void List::Shift(TContext ctx, TObject this_object, size_t argc, const TValue else { auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(0)); - return_value.set(RealmObject::create(ctx, realm_object)); + return_value.set(RealmObject::create_instance(ctx, realm_object)); list->remove(0); } } @@ -193,7 +193,7 @@ void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValu for (size_t i = 0; i < remove; i++) { auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); - removed_objects.push_back(RealmObject::create(ctx, realm_object)); + removed_objects.push_back(RealmObject::create_instance(ctx, realm_object)); list->remove(index); } for (size_t i = 2; i < argc; i++) { @@ -208,7 +208,7 @@ void List::StaticResults(TContext ctx, TObject this_object, size_t argc, cons validate_argument_count(argc, 0); auto list = get_internal(this_object); - return_value.set(Results::create(ctx, *list, false)); + return_value.set(Results::create_instance(ctx, *list, false)); } template diff --git a/src/js_object.hpp b/src/js_object.hpp index 7e1316e6..1e3eced5 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -40,7 +40,7 @@ struct RealmObject { using Function = Function; using ReturnValue = ReturnValue; - static TObject create(TContext, realm::Object &); + static TObject create_instance(TContext, realm::Object &); static void GetProperty(TContext, TObject, const String &, ReturnValue &); static bool SetProperty(TContext, TObject, const String &, TValue); @@ -61,7 +61,7 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object RealmObject::create(TContext ctx, realm::Object &realm_object) { +typename T::Object RealmObject::create_instance(TContext ctx, realm::Object &realm_object) { static String s_prototype = "prototype"; auto delegate = get_delegate(realm_object.realm().get()); diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index 6a28f42e..1c950526 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -122,7 +122,7 @@ struct NativeAccessor { throw std::runtime_error("object is not a Realm Object"); } static TValue from_object(TContext ctx, realm::Object realm_object) { - return RealmObject::create(ctx, realm_object); + return RealmObject::create_instance(ctx, realm_object); } static size_t list_size(TContext ctx, TValue &value) { @@ -132,7 +132,7 @@ struct NativeAccessor { return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index); } static TValue from_list(TContext ctx, realm::List list) { - return List::create(ctx, list); + return List::create_instance(ctx, list); } static Mixed to_mixed(TContext ctx, TValue &val) { diff --git a/src/js_realm.hpp b/src/js_realm.hpp index e4784ea3..12e9b154 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -347,7 +347,7 @@ void Realm::Objects(TContext ctx, TObject this_object, size_t argc, const TVa SharedRealm realm = *get_internal(this_object); std::string type = validated_object_type_for_value(realm, ctx, arguments[0]); - return_value.set(Results::create(ctx, realm, type)); + return_value.set(Results::create_instance(ctx, realm, type)); } template @@ -374,7 +374,7 @@ void Realm::Create(TContext ctx, TObject this_object, size_t argc, const TVal } auto realm_object = realm::Object::create(ctx, sharedRealm, *object_schema, object, update); - return_value.set(RealmObject::create(ctx, realm_object)); + return_value.set(RealmObject::create_instance(ctx, realm_object)); } template diff --git a/src/js_results.hpp b/src/js_results.hpp index b8e7822f..ac2b4c15 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -38,10 +38,10 @@ struct Results { using Value = Value; using ReturnValue = ReturnValue; - static TObject create(TContext, const realm::Results &, bool live = true); - static TObject create(TContext, const realm::List &, bool live = true); - static TObject create(TContext, SharedRealm, const std::string &type, bool live = true); - static TObject create(TContext, SharedRealm, const ObjectSchema &, Query, bool live = true); + static TObject create_instance(TContext, const realm::Results &, bool live = true); + static TObject create_instance(TContext, const realm::List &, bool live = true); + static TObject create_instance(TContext, SharedRealm, const std::string &type, bool live = true); + static TObject create_instance(TContext, SharedRealm, const ObjectSchema &, Query, bool live = true); template static TObject create_filtered(TContext, const U &, size_t, const TValue[]); @@ -77,7 +77,7 @@ struct ObjectClass : BaseObjectClass { }; template -typename T::Object Results::create(TContext ctx, const realm::Results &results, bool live) { +typename T::Object Results::create_instance(TContext ctx, const realm::Results &results, bool live) { auto new_results = new realm::Results(results); new_results->set_live(live); @@ -85,12 +85,12 @@ typename T::Object Results::create(TContext ctx, const realm::Results &result } template -typename T::Object Results::create(TContext ctx, const realm::List &list, bool live) { - return create(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live); +typename T::Object Results::create_instance(TContext ctx, const realm::List &list, bool live) { + return create_instance(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live); } template -typename T::Object Results::create(TContext ctx, SharedRealm realm, const std::string &type, bool live) { +typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, const std::string &type, bool live) { auto table = ObjectStore::table_for_object_type(realm->read_group(), type); auto &schema = realm->config().schema; auto object_schema = schema->find(type); @@ -106,7 +106,7 @@ typename T::Object Results::create(TContext ctx, SharedRealm realm, const std } template -typename T::Object Results::create(TContext ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { +typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { auto results = new realm::Results(realm, object_schema, std::move(query)); results->set_live(live); @@ -132,7 +132,7 @@ typename T::Object Results::create_filtered(TContext ctx, const U &collection query_builder::ArgumentConverter converter(ctx, args); query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name); - return create(ctx, realm, object_schema, std::move(query)); + return create_instance(ctx, realm, object_schema, std::move(query)); } template @@ -211,7 +211,7 @@ void Results::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnVa } auto realm_object = realm::Object(results->get_realm(), results->get_object_schema(), results->get(index)); - return_value.set(RealmObject::create(ctx, realm_object)); + return_value.set(RealmObject::create_instance(ctx, realm_object)); } template @@ -219,7 +219,7 @@ void Results::StaticResults(TContext ctx, TObject this_object, size_t argc, c validate_argument_count(argc, 0); auto results = get_internal(this_object); - return_value.set(Results::create(ctx, *results, false)); + return_value.set(Results::create_instance(ctx, *results, false)); } template diff --git a/src/js_types.hpp b/src/js_types.hpp index cb591612..e53da2ca 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -220,7 +220,7 @@ struct Object { static TObject create_date(TContext, double); template - static TObject create(TContext, U*); + static TObject create_instance(TContext, U*); template static bool is_instance(TContext, const TObject &); @@ -282,7 +282,7 @@ struct ReturnValue { template REALM_JS_INLINE typename T::Object create_object(typename T::Context ctx, U* internal = nullptr) { - return Object::template create(ctx, internal); + return Object::template create_instance(ctx, internal); } template diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 7d6982c3..811a0473 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -51,7 +51,7 @@ class ObjectWrap { return nullptr; } - JSObjectRef this_object = ObjectWrap::create(ctx); + JSObjectRef this_object = ObjectWrap::create_instance(ctx); try { s_class.constructor(ctx, this_object, argc, arguments); } @@ -265,7 +265,7 @@ class ObjectWrap { return js_class; } - static JSObjectRef create(JSContextRef ctx, T* internal = nullptr) { + static JSObjectRef create_instance(JSContextRef ctx, T* internal = nullptr) { return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); } diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index 470aed2b..04dd1867 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -482,8 +482,8 @@ inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) { template<> template -inline JSObjectRef jsc::Object::create(JSContextRef ctx, U* internal) { - return jsc::ObjectWrap::create(ctx, internal); +inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, U* internal) { + return jsc::ObjectWrap::create_instance(ctx, internal); } template<> diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 6f3aeff4..ef672022 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -88,7 +88,7 @@ class ObjectWrap : public Nan::ObjectWrap { return *this; } - static v8::Local create(v8::Isolate* isolate, T* internal = nullptr) { + static v8::Local create_instance(v8::Isolate* isolate, T* internal = nullptr) { Nan::EscapableHandleScope scope; // TODO: Figure out why this template ends up being empty here. diff --git a/src/node/node_types.hpp b/src/node/node_types.hpp index b00b4d87..95643197 100644 --- a/src/node/node_types.hpp +++ b/src/node/node_types.hpp @@ -441,8 +441,8 @@ inline v8::Local node::Object::create_date(v8::Isolate* isolate, dou template<> template -inline v8::Local node::Object::create(v8::Isolate* isolate, U* internal) { - return node::ObjectWrap::create(isolate, internal); +inline v8::Local node::Object::create_instance(v8::Isolate* isolate, U* internal) { + return node::ObjectWrap::create_instance(isolate, internal); } template<> From e8ca5ff92e306b729946e9fef8333b446b043127 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 15 Apr 2016 10:50:15 -0700 Subject: [PATCH 19/48] ObjectClass -> ClassDefinition --- src/js_class.hpp | 8 ++++---- src/js_collection.hpp | 2 +- src/js_list.hpp | 2 +- src/js_object.hpp | 2 +- src/js_realm.hpp | 2 +- src/js_results.hpp | 2 +- src/jsc/jsc_class.hpp | 10 +++++----- src/jsc/jsc_types.hpp | 2 +- src/node/node_class.hpp | 6 +++--- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/js_class.hpp b/src/js_class.hpp index 2a80e927..1de62dba 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -69,17 +69,17 @@ template using PropertyMap = std::map>; template -struct ObjectClass { +struct ClassDefinition { // Every specialization *must* at least have a name. std::string name; }; template -struct BaseObjectClass { +struct BaseClassDefinition { // This pointer does not need to be set. - ObjectClass* superclass; + ClassDefinition* superclass; - // ObjectClass specializations should inherit from this class and override what's needed below. + // ClassDefinition specializations should inherit from this class and override what's needed below. ConstructorType* constructor; MethodMap static_methods; PropertyMap static_properties; diff --git a/src/js_collection.hpp b/src/js_collection.hpp index 1a1c5f02..58b8b8b0 100644 --- a/src/js_collection.hpp +++ b/src/js_collection.hpp @@ -27,7 +27,7 @@ namespace js { class Collection {}; template -struct ObjectClass : BaseObjectClass { +struct ClassDefinition : BaseClassDefinition { std::string const name = "Collection"; }; diff --git a/src/js_list.hpp b/src/js_list.hpp index 167521c5..b4283c87 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -58,7 +58,7 @@ struct List { }; template -struct ObjectClass : BaseObjectClass { +struct ClassDefinition : BaseClassDefinition { using List = List; std::string const name = "List"; diff --git a/src/js_object.hpp b/src/js_object.hpp index 1e3eced5..f2514790 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -48,7 +48,7 @@ struct RealmObject { }; template -struct ObjectClass : BaseObjectClass { +struct ClassDefinition : BaseClassDefinition { using RealmObject = RealmObject; const std::string name = "RealmObject"; diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 12e9b154..b276661c 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -186,7 +186,7 @@ class Realm { }; template -struct ObjectClass : BaseObjectClass { +struct ClassDefinition : BaseClassDefinition { using Realm = Realm; std::string const name = "Realm"; diff --git a/src/js_results.hpp b/src/js_results.hpp index ac2b4c15..27865834 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -58,7 +58,7 @@ struct Results { }; template -struct ObjectClass : BaseObjectClass { +struct ClassDefinition : BaseClassDefinition { using Results = Results; std::string const name = "Results"; diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 811a0473..8109db8c 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -26,9 +26,9 @@ namespace realm { namespace jsc { template -using ObjectClass = js::ObjectClass; +using ClassDefinition = js::ClassDefinition; -using BaseObjectClass = js::BaseObjectClass; +using BaseClassDefinition = js::BaseClassDefinition; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; using PropertyType = js::PropertyType; @@ -39,7 +39,7 @@ using PropertyMap = js::PropertyMap; template class ObjectWrap { - static ObjectClass s_class; + static ClassDefinition s_class; std::unique_ptr m_object; @@ -147,7 +147,7 @@ class ObjectWrap { } template - static JSClassRef get_superclass(ObjectClass*) { + static JSClassRef get_superclass(ClassDefinition*) { return ObjectWrap::get_class(); } @@ -289,7 +289,7 @@ inline JSClassRef ObjectWrap::get_class() { } // The declared static variables must be defined as well. -template ObjectClass ObjectWrap::s_class; +template ClassDefinition ObjectWrap::s_class; } // jsc diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index 04dd1867..dcfdc27f 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -30,7 +30,7 @@ namespace jsc { struct Types { using Context = JSContextRef; using GlobalContext = JSGlobalContextRef; - using ObjectClass = JSClassRef; + using ClassDefinition = JSClassRef; using Value = JSValueRef; using Object = JSObjectRef; using String = JSStringRef; diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index ef672022..27e93d81 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -25,7 +25,7 @@ namespace realm { namespace node { template -using ObjectClass = js::ObjectClass; +using ClassDefinition = js::ClassDefinition; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; @@ -69,7 +69,7 @@ static inline void setup_property(v8::Local tpl, const std:: template class ObjectWrap : public Nan::ObjectWrap { - static ObjectClass s_class; + static ClassDefinition s_class; static Nan::Persistent s_constructor; static Nan::Persistent s_template; @@ -165,7 +165,7 @@ class ObjectWrap : public Nan::ObjectWrap { }; // The declared static variables must be defined as well. -template ObjectClass ObjectWrap::s_class; +template ClassDefinition ObjectWrap::s_class; template Nan::Persistent ObjectWrap::s_constructor; template Nan::Persistent ObjectWrap::s_template; From 760126f0d7eecddaae2d8a5e3d6d4570874524a7 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 15 Apr 2016 11:59:10 -0700 Subject: [PATCH 20/48] public before private --- src/jsc/jsc_class.hpp | 75 ++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 8109db8c..ff246f58 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -39,6 +39,44 @@ using PropertyMap = js::PropertyMap; template class ObjectWrap { +public: + operator T*() const { + return m_object.get(); + } + ObjectWrap& operator=(T* object) { + if (m_object.get() != object) { + m_object = std::unique_ptr(object); + } + return *this; + } + + static JSClassRef get_class() { + static JSClassRef js_class = create_class(); + return js_class; + } + + static JSClassRef get_constructor_class() { + static JSClassRef js_class = create_constructor_class(); + return js_class; + } + + static JSObjectRef create_instance(JSContextRef ctx, T* internal = nullptr) { + return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); + } + + static JSObjectRef create_constructor(JSContextRef ctx) { + if (JSClassRef constructor_class = get_constructor_class()) { + return JSObjectMake(ctx, constructor_class, nullptr); + } + + return JSObjectMakeConstructor(ctx, get_class(), construct); + } + + static bool has_instance(JSContextRef ctx, JSValueRef value) { + return JSValueIsObjectOfClass(ctx, value, get_class()); + } + +private: static ClassDefinition s_class; std::unique_ptr m_object; @@ -243,43 +281,6 @@ class ObjectWrap { return JSClassCreate(&definition); } - - public: - operator T*() const { - return m_object.get(); - } - ObjectWrap& operator=(T* object) { - if (m_object.get() != object) { - m_object = std::unique_ptr(object); - } - return *this; - } - - static JSClassRef get_class() { - static JSClassRef js_class = create_class(); - return js_class; - } - - static JSClassRef get_constructor_class() { - static JSClassRef js_class = create_constructor_class(); - return js_class; - } - - static JSObjectRef create_instance(JSContextRef ctx, T* internal = nullptr) { - return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); - } - - static JSObjectRef create_constructor(JSContextRef ctx) { - if (JSClassRef constructor_class = get_constructor_class()) { - return JSObjectMake(ctx, constructor_class, nullptr); - } - - return JSObjectMakeConstructor(ctx, get_class(), construct); - } - - static bool has_instance(JSContextRef ctx, JSValueRef value) { - return JSValueIsObjectOfClass(ctx, value, get_class()); - } }; // Make the top-level base class return a NULL JSClassRef. From bd4088ce91a6bb949589646b28d3802a583d253d Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 15 Apr 2016 13:47:01 -0700 Subject: [PATCH 21/48] key templates off explicitly defined classes rather than the internal type --- src/js_class.hpp | 4 ++- src/js_collection.hpp | 2 +- src/js_list.hpp | 26 ++++++++-------- src/js_object.hpp | 10 +++--- src/js_object_accessor.hpp | 8 ++--- src/js_realm.hpp | 55 ++++++++++++++++---------------- src/js_results.hpp | 20 ++++++------ src/js_types.hpp | 32 +++++++++---------- src/jsc/jsc_class.hpp | 64 +++++++++++++++++++++++++++----------- src/jsc/jsc_types.hpp | 22 ++++++------- src/rpc.cpp | 19 +++++------ 11 files changed, 148 insertions(+), 114 deletions(-) diff --git a/src/js_class.hpp b/src/js_class.hpp index 1de62dba..0e6eb112 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -70,6 +70,8 @@ using PropertyMap = std::map>; template struct ClassDefinition { + using Internal = U; + // Every specialization *must* at least have a name. std::string name; }; @@ -89,7 +91,7 @@ struct BaseClassDefinition { StringPropertyType string_accessor; }; -template +template class ObjectWrap; } // js diff --git a/src/js_collection.hpp b/src/js_collection.hpp index 58b8b8b0..2ed53915 100644 --- a/src/js_collection.hpp +++ b/src/js_collection.hpp @@ -27,7 +27,7 @@ namespace js { class Collection {}; template -struct ClassDefinition : BaseClassDefinition { +struct CollectionClass : ClassDefinition, BaseClassDefinition { std::string const name = "Collection"; }; diff --git a/src/js_list.hpp b/src/js_list.hpp index b4283c87..a1152915 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -58,7 +58,7 @@ struct List { }; template -struct ClassDefinition : BaseClassDefinition { +struct ListClass : ClassDefinition, BaseClassDefinition> { using List = List; std::string const name = "List"; @@ -83,18 +83,18 @@ struct ClassDefinition : BaseClassDefinition { template typename T::Object List::create_instance(TContext ctx, realm::List &list) { - return create_object(ctx, new realm::List(list)); + return create_object>(ctx, new realm::List(list)); } template void List::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { - auto list = get_internal(object); + auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } template void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { - auto list = get_internal(object); + auto list = get_internal>(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); return_value.set(RealmObject::create_instance(ctx, realm_object)); @@ -102,7 +102,7 @@ void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue template bool List::SetIndex(TContext ctx, TObject object, uint32_t index, TValue value) { - auto list = get_internal(object); + auto list = get_internal>(object); list->set(ctx, value, index); return true; } @@ -111,7 +111,7 @@ template void List::Push(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); for (size_t i = 0; i < argc; i++) { list->add(ctx, arguments[i]); } @@ -123,7 +123,7 @@ template void List::Pop(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); size_t size = list->size(); if (size == 0) { list->verify_in_transaction(); @@ -142,7 +142,7 @@ template void List::Unshift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); for (size_t i = 0; i < argc; i++) { list->insert(ctx, arguments[i], i); } @@ -154,7 +154,7 @@ template void List::Shift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); if (list->size() == 0) { list->verify_in_transaction(); return_value.set_undefined(); @@ -171,7 +171,7 @@ template void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); size_t size = list->size(); long index = std::min(Value::to_number(ctx, arguments[0]), size); if (index < 0) { @@ -207,7 +207,7 @@ template void List::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); return_value.set(Results::create_instance(ctx, *list, false)); } @@ -215,7 +215,7 @@ template void List::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); return_value.set(Results::create_filtered(ctx, *list, argc, arguments)); } @@ -223,7 +223,7 @@ template void List::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); - auto list = get_internal(this_object); + auto list = get_internal>(this_object); return_value.set(Results::create_sorted(ctx, *list, argc, arguments)); } diff --git a/src/js_object.hpp b/src/js_object.hpp index f2514790..f79ddbce 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -48,7 +48,7 @@ struct RealmObject { }; template -struct ClassDefinition : BaseClassDefinition { +struct RealmObjectClass : ClassDefinition, BaseClassDefinition { using RealmObject = RealmObject; const std::string name = "RealmObject"; @@ -66,7 +66,7 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & auto delegate = get_delegate(realm_object.realm().get()); auto name = realm_object.get_object_schema().name; - auto object = create_object(ctx, new realm::Object(realm_object)); + auto object = create_object>(ctx, new realm::Object(realm_object)); if (!delegate->m_constructors.count(name)) { return object; @@ -87,7 +87,7 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & template void RealmObject::GetProperty(TContext ctx, TObject object, const String &property, ReturnValue &return_value) { try { - auto realm_object = get_internal(object); + auto realm_object = get_internal>(object); auto result = realm_object->template get_property_value(ctx, property); return_value.set(result); } catch (InvalidPropertyException &ex) { @@ -97,14 +97,14 @@ void RealmObject::GetProperty(TContext ctx, TObject object, const String &pro template bool RealmObject::SetProperty(TContext ctx, TObject object, const String &property, TValue value) { - auto realm_object = get_internal(object); + auto realm_object = get_internal>(object); realm_object->set_property_value(ctx, property, value, true); return true; } template std::vector> RealmObject::GetPropertyNames(TContext ctx, TObject object) { - auto realm_object = get_internal(object); + auto realm_object = get_internal>(object); auto &properties = realm_object->get_object_schema().properties; std::vector names; diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index 1c950526..8f3ed85f 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -102,8 +102,8 @@ struct NativeAccessor { static size_t to_object_index(TContext ctx, SharedRealm realm, TValue &value, const std::string &type, bool try_update) { TObject object = Value::validated_to_object(ctx, value); - if (Object::template is_instance(ctx, object)) { - return get_internal(object)->row().get_index(); + if (Object::template is_instance>(ctx, object)) { + return get_internal>(object)->row().get_index(); } auto object_schema = realm->config().schema->find(type); @@ -116,8 +116,8 @@ struct NativeAccessor { } static size_t to_existing_object_index(TContext ctx, TValue &value) { TObject object = Value::validated_to_object(ctx, value); - if (Object::template is_instance(ctx, object)) { - return get_internal(object)->row().get_index(); + if (Object::template is_instance>(ctx, object)) { + return get_internal>(object)->row().get_index(); } throw std::runtime_error("object is not a Realm Object"); } diff --git a/src/js_realm.hpp b/src/js_realm.hpp index b276661c..4233d4af 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -37,6 +37,9 @@ namespace realm { namespace js { +template +struct RealmClass; + template class RealmDelegate : public BindingContext { public: @@ -87,7 +90,7 @@ class RealmDelegate : public BindingContext { throw std::runtime_error("Realm no longer exists"); } - TObject realm_object = create_object(m_context, new SharedRealm(realm)); + TObject realm_object = create_object>(m_context, new SharedRealm(realm)); TValue arguments[2]; arguments[0] = realm_object; arguments[1] = Value::from_string(m_context, notification_name); @@ -140,10 +143,10 @@ class Realm { static void SetDefaultPath(TContext, TObject, TValue value); static TObject create_constructor(TContext ctx) { - TObject realm_constructor = ObjectWrap::create_constructor(ctx); - TObject collection_constructor = ObjectWrap::create_constructor(ctx); - TObject list_constructor = ObjectWrap::create_constructor(ctx); - TObject results_constructor = ObjectWrap::create_constructor(ctx); + TObject realm_constructor = ObjectWrap>::create_constructor(ctx); + TObject collection_constructor = ObjectWrap>::create_constructor(ctx); + TObject list_constructor = ObjectWrap>::create_constructor(ctx); + TObject results_constructor = ObjectWrap>::create_constructor(ctx); PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); @@ -186,7 +189,7 @@ class Realm { }; template -struct ClassDefinition : BaseClassDefinition { +struct RealmClass : ClassDefinition, BaseClassDefinition { using Realm = Realm; std::string const name = "Realm"; @@ -288,7 +291,7 @@ void Realm::Constructor(TContext ctx, TObject this_object, size_t argc, const delegate->m_defaults = std::move(defaults); delegate->m_constructors = std::move(constructors); - set_internal(this_object, new SharedRealm(realm)); + set_internal>(this_object, new SharedRealm(realm)); } template @@ -330,13 +333,13 @@ void Realm::SetDefaultPath(TContext ctx, TObject object, TValue value) { template void Realm::GetPath(TContext ctx, TObject object, ReturnValue &return_value) { - std::string path = get_internal(object)->get()->config().path; + std::string path = get_internal>(object)->get()->config().path; return_value.set(path); } template void Realm::GetSchemaVersion(TContext ctx, TObject object, ReturnValue &return_value) { - double version = get_internal(object)->get()->config().schema_version; + double version = get_internal>(object)->get()->config().schema_version; return_value.set(version); } @@ -344,7 +347,7 @@ template void Realm::Objects(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); std::string type = validated_object_type_for_value(realm, ctx, arguments[0]); return_value.set(Results::create_instance(ctx, realm, type)); @@ -354,7 +357,7 @@ template void Realm::Create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2, 3); - SharedRealm sharedRealm = *get_internal(this_object); + SharedRealm sharedRealm = *get_internal>(this_object); std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); auto &schema = sharedRealm->config().schema; auto object_schema = schema->find(className); @@ -381,15 +384,15 @@ template void Realm::Delete(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); if (!realm->is_in_transaction()) { throw std::runtime_error("Can only delete objects within a transaction."); } TObject arg = Value::validated_to_object(ctx, arguments[0]); - if (Object::template is_instance(ctx, arg)) { - auto object = get_internal(arg); + if (Object::template is_instance>(ctx, arg)) { + auto object = get_internal>(arg); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object->get_object_schema().name); table->move_last_over(object->row().get_index()); } @@ -398,21 +401,21 @@ void Realm::Delete(TContext ctx, TObject this_object, size_t argc, const TVal for (uint32_t i = length; i--;) { TObject object = Object::validated_get_object(ctx, arg, i); - if (!Object::template is_instance(ctx, object)) { + if (!Object::template is_instance>(ctx, object)) { throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); } - auto realm_object = get_internal(object); + auto realm_object = get_internal>(object); realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), realm_object->get_object_schema().name); table->move_last_over(realm_object->row().get_index()); } } - else if (Object::template is_instance(ctx, arg)) { - auto results = get_internal(arg); + else if (Object::template is_instance>(ctx, arg)) { + auto results = get_internal>(arg); results->clear(); } - else if (Object::template is_instance(ctx, arg)) { - auto list = get_internal(arg); + else if (Object::template is_instance>(ctx, arg)) { + auto list = get_internal>(arg); list->delete_all(); } else { @@ -424,7 +427,7 @@ template void Realm::DeleteAll(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); if (!realm->is_in_transaction()) { throw std::runtime_error("Can only delete objects within a transaction."); @@ -439,7 +442,7 @@ template void Realm::Write(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); TFunction callback = Value::validated_to_function(ctx, arguments[0]); try { @@ -462,7 +465,7 @@ void Realm::AddListener(TContext ctx, TObject this_object, size_t argc, const __unused std::string name = validated_notification_name(ctx, arguments[0]); auto callback = Value::validated_to_function(ctx, arguments[1]); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); get_delegate(realm.get())->add_notification(callback); } @@ -473,7 +476,7 @@ void Realm::RemoveListener(TContext ctx, TObject this_object, size_t argc, co __unused std::string name = validated_notification_name(ctx, arguments[0]); auto callback = Value::validated_to_function(ctx, arguments[1]); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); get_delegate(realm.get())->remove_notification(callback); } @@ -484,7 +487,7 @@ void Realm::RemoveAllListeners(TContext ctx, TObject this_object, size_t argc validated_notification_name(ctx, arguments[0]); } - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); get_delegate(realm.get())->remove_all_notifications(); } @@ -492,7 +495,7 @@ template void Realm::Close(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - SharedRealm realm = *get_internal(this_object); + SharedRealm realm = *get_internal>(this_object); realm->close(); } diff --git a/src/js_results.hpp b/src/js_results.hpp index 27865834..a0577b3a 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -58,7 +58,7 @@ struct Results { }; template -struct ClassDefinition : BaseClassDefinition { +struct ResultsClass : ClassDefinition, BaseClassDefinition> { using Results = Results; std::string const name = "Results"; @@ -81,7 +81,7 @@ typename T::Object Results::create_instance(TContext ctx, const realm::Result auto new_results = new realm::Results(results); new_results->set_live(live); - return create_object(ctx, new_results); + return create_object>(ctx, new_results); } template @@ -102,7 +102,7 @@ typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, auto results = new realm::Results(realm, *object_schema, *table); results->set_live(live); - return create_object(ctx, results); + return create_object>(ctx, results); } template @@ -110,7 +110,7 @@ typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, auto results = new realm::Results(realm, object_schema, std::move(query)); results->set_live(live); - return create_object(ctx, results); + return create_object>(ctx, results); } template @@ -190,18 +190,18 @@ typename T::Object Results::create_sorted(TContext ctx, const U &collection, } auto results = new realm::Results(realm, object_schema, collection.get_query(), {std::move(columns), std::move(ascending)}); - return create_object(ctx, results); + return create_object>(ctx, results); } template void Results::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { - auto results = get_internal(object); + auto results = get_internal>(object); return_value.set((uint32_t)results->size()); } template void Results::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { - auto results = get_internal(object); + auto results = get_internal>(object); auto row = results->get(index); // Return null for deleted objects in a snapshot. @@ -218,7 +218,7 @@ template void Results::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - auto results = get_internal(this_object); + auto results = get_internal>(this_object); return_value.set(Results::create_instance(ctx, *results, false)); } @@ -226,7 +226,7 @@ template void Results::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); - auto results = get_internal(this_object); + auto results = get_internal>(this_object); return_value.set(create_filtered(ctx, *results, argc, arguments)); } @@ -234,7 +234,7 @@ template void Results::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); - auto results = get_internal(this_object); + auto results = get_internal>(this_object); return_value.set(create_sorted(ctx, *results, argc, arguments)); } diff --git a/src/js_types.hpp b/src/js_types.hpp index e53da2ca..a9697efc 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -219,17 +219,17 @@ struct Object { static TObject create_date(TContext, double); - template - static TObject create_instance(TContext, U*); + template + static TObject create_instance(TContext, typename ClassType::Internal*); - template + template static bool is_instance(TContext, const TObject &); - template - static U* get_internal(const TObject &); + template + static typename ClassType::Internal* get_internal(const TObject &); - template - static void set_internal(const TObject &, U*); + template + static void set_internal(const TObject &, typename ClassType::Internal*); }; template @@ -280,19 +280,19 @@ struct ReturnValue { void set_undefined(); }; -template -REALM_JS_INLINE typename T::Object create_object(typename T::Context ctx, U* internal = nullptr) { - return Object::template create_instance(ctx, internal); +template +REALM_JS_INLINE typename T::Object create_object(typename T::Context ctx, typename ClassType::Internal* internal = nullptr) { + return Object::template create_instance(ctx, internal); } -template -REALM_JS_INLINE U* get_internal(const typename T::Object &object) { - return Object::template get_internal(object); +template +REALM_JS_INLINE typename ClassType::Internal* get_internal(const typename T::Object &object) { + return Object::template get_internal(object); } -template -REALM_JS_INLINE void set_internal(const typename T::Object &object, U* ptr) { - Object::template set_internal(object, ptr); +template +REALM_JS_INLINE void set_internal(const typename T::Object &object, typename ClassType::Internal* ptr) { + Object::template set_internal(object, ptr); } } // js diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index ff246f58..3299fc5c 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -28,7 +28,6 @@ namespace jsc { template using ClassDefinition = js::ClassDefinition; -using BaseClassDefinition = js::BaseClassDefinition; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; using PropertyType = js::PropertyType; @@ -37,15 +36,16 @@ using StringPropertyType = js::StringPropertyType; using MethodMap = js::MethodMap; using PropertyMap = js::PropertyMap; -template +template class ObjectWrap { public: - operator T*() const { + using Internal = typename ClassType::Internal; + operator Internal*() const { return m_object.get(); } - ObjectWrap& operator=(T* object) { + ObjectWrap& operator=(Internal* object) { if (m_object.get() != object) { - m_object = std::unique_ptr(object); + m_object = std::unique_ptr(object); } return *this; } @@ -60,8 +60,8 @@ public: return js_class; } - static JSObjectRef create_instance(JSContextRef ctx, T* internal = nullptr) { - return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); + static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { + return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); } static JSObjectRef create_constructor(JSContextRef ctx) { @@ -77,11 +77,11 @@ public: } private: - static ClassDefinition s_class; + static ClassType s_class; - std::unique_ptr m_object; + std::unique_ptr m_object; - ObjectWrap(T* object = nullptr) : m_object(object) {} + ObjectWrap(Internal* object = nullptr) : m_object(object) {} static JSObjectRef construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { if (!s_class.constructor) { @@ -89,7 +89,7 @@ private: return nullptr; } - JSObjectRef this_object = ObjectWrap::create_instance(ctx); + JSObjectRef this_object = ObjectWrap::create_instance(ctx); try { s_class.constructor(ctx, this_object, argc, arguments); } @@ -178,7 +178,7 @@ private: static void finalize(JSObjectRef object) { // This is called for the most derived class before superclasses. - if (auto wrap = static_cast *>(JSObjectGetPrivate(object))) { + if (auto wrap = static_cast *>(JSObjectGetPrivate(object))) { delete wrap; JSObjectSetPrivate(object, nullptr); } @@ -283,14 +283,42 @@ private: } }; -// Make the top-level base class return a NULL JSClassRef. template<> -inline JSClassRef ObjectWrap::get_class() { - return nullptr; -} - +class ObjectWrap { +public: + using Internal = void; + + operator Internal*() const { + return nullptr; + } + + ObjectWrap& operator=(Internal* object) { + return *this; + } + + static JSClassRef get_class() { + return nullptr; + } + + static JSClassRef get_constructor_class() { + return nullptr; + } + + static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { + return nullptr; + } + + static JSObjectRef create_constructor(JSContextRef ctx) { + return nullptr; + } + + static bool has_instance(JSContextRef ctx, JSValueRef value) { + return nullptr; + } +}; + // The declared static variables must be defined as well. -template ClassDefinition ObjectWrap::s_class; +template T ObjectWrap::s_class; } // jsc diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index dcfdc27f..039b9c27 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -481,27 +481,27 @@ inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) { } template<> -template -inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, U* internal) { - return jsc::ObjectWrap::create_instance(ctx, internal); +template +inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, typename ClassType::Internal* internal) { + return jsc::ObjectWrap::create_instance(ctx, internal); } template<> -template +template inline bool jsc::Object::is_instance(JSContextRef ctx, const JSObjectRef &object) { - return jsc::ObjectWrap::has_instance(ctx, object); + return jsc::ObjectWrap::has_instance(ctx, object); } template<> -template -inline U* jsc::Object::get_internal(const JSObjectRef &object) { - return *static_cast *>(JSObjectGetPrivate(object)); +template +inline typename ClassType::Internal* jsc::Object::get_internal(const JSObjectRef &object) { + return *static_cast *>(JSObjectGetPrivate(object)); } template<> -template -inline void jsc::Object::set_internal(const JSObjectRef &object, U* ptr) { - auto wrap = static_cast *>(JSObjectGetPrivate(object)); +template +inline void jsc::Object::set_internal(const JSObjectRef &object, typename ClassType::Internal* ptr) { + auto wrap = static_cast *>(JSObjectGetPrivate(object)); *wrap = ptr; } diff --git a/src/rpc.cpp b/src/rpc.cpp index f3320d33..bd50187f 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -24,6 +24,7 @@ #include "rpc.hpp" #include "jsc_init.hpp" +#include "jsc_types.hpp" #include "js_object.hpp" #include "js_results.hpp" #include "js_realm.hpp" @@ -86,21 +87,21 @@ RPCServer::RPCServer() { }; m_requests["/begin_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + SharedRealm realm = *jsc::Object::get_internal>(m_objects[realm_id]); realm->begin_transaction(); return json::object(); }; m_requests["/cancel_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + SharedRealm realm = *jsc::Object::get_internal>(m_objects[realm_id]); realm->cancel_transaction(); return json::object(); }; m_requests["/commit_transaction"] = [this](const json dict) { RPCObjectID realm_id = dict["realmId"].get(); - SharedRealm realm = *jsc::Object::get_internal(m_objects[realm_id]); + SharedRealm realm = *jsc::Object::get_internal>(m_objects[realm_id]); realm->commit_transaction(); return json::object(); @@ -220,16 +221,16 @@ json RPCServer::serialize_json_value(JSValueRef js_value) { JSObjectRef js_object = jsc::Value::validated_to_object(m_context, js_value); - if (jsc::Object::is_instance(m_context, js_object)) { - auto object = jsc::Object::get_internal(js_object); + if (jsc::Object::is_instance>(m_context, js_object)) { + auto object = jsc::Object::get_internal>(js_object); return { {"type", RealmObjectTypesObject}, {"id", store_object(js_object)}, {"schema", serialize_object_schema(object->get_object_schema())} }; } - else if (jsc::Object::is_instance(m_context, js_object)) { - auto list = jsc::Object::get_internal(js_object); + else if (jsc::Object::is_instance>(m_context, js_object)) { + auto list = jsc::Object::get_internal>(js_object); return { {"type", RealmObjectTypesList}, {"id", store_object(js_object)}, @@ -237,8 +238,8 @@ json RPCServer::serialize_json_value(JSValueRef js_value) { {"schema", serialize_object_schema(list->get_object_schema())} }; } - else if (jsc::Object::is_instance(m_context, js_object)) { - auto results = jsc::Object::get_internal(js_object); + else if (jsc::Object::is_instance>(m_context, js_object)) { + auto results = jsc::Object::get_internal>(js_object); return { {"type", RealmObjectTypesResults}, {"id", store_object(js_object)}, From 133289ad85f3386a528fa5e0ea73eeb81aec4bee Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Thu, 14 Apr 2016 12:39:17 -0700 Subject: [PATCH 22/48] All tests now pass on Node --- binding.gyp | 1 - lib/index.js | 21 ++- package.json | 1 + src/ios/RealmJS.xcodeproj/project.pbxproj | 14 +- src/js_realm.hpp | 28 ++- src/js_types.hpp | 4 +- src/jsc/jsc_class.hpp | 40 +--- src/jsc/jsc_init.cpp | 33 +--- src/jsc/jsc_types.hpp | 17 +- src/node/node_class.hpp | 218 ++++++++++++++++------ src/node/{node_dummy.c => node_dummy.cpp} | 13 +- src/node/node_init.cpp | 11 +- src/node/node_object_accessor.cpp | 36 ---- src/node/node_object_accessor.hpp | 45 +++++ src/node/node_types.hpp | 42 +++-- tests/index.js | 38 ++++ tests/js/query-tests.json | 1 + 17 files changed, 348 insertions(+), 215 deletions(-) rename src/node/{node_dummy.c => node_dummy.cpp} (65%) delete mode 100644 src/node/node_object_accessor.cpp create mode 100644 tests/index.js create mode 120000 tests/js/query-tests.json diff --git a/binding.gyp b/binding.gyp index a259f902..77bcf04c 100644 --- a/binding.gyp +++ b/binding.gyp @@ -5,7 +5,6 @@ "sources": [ "src/js_realm.cpp", "src/node/node_init.cpp", - "src/node/node_object_accessor.cpp", "src/object-store/src/index_set.cpp", "src/object-store/src/list.cpp", "src/object-store/src/object_schema.cpp", diff --git a/lib/index.js b/lib/index.js index 9ac4465f..b8aa002c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,9 +24,13 @@ var realmConstructor; if (typeof Realm != 'undefined') { // The global Realm constructor should be available on device (using JavaScriptCore). realmConstructor = Realm; // eslint-disable-line no-undef -} else if (typeof navigator != 'undefined' && navigator.userAgent) { // eslint-disable-line no-undef +// eslint-disable-next-line +} else if (typeof navigator != 'undefined' && navigator.userAgent) { // The userAgent will be defined when running in a browser (such as Chrome debugging mode). realmConstructor = require('./browser').default; // (exported as ES6 module) +// eslint-disable-next-line +} else if (typeof process == 'object' && ('' + process) == '[object process]') { + realmConstructor = require('bindings')('realm').Realm; } else { throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!'); } @@ -34,4 +38,19 @@ if (typeof Realm != 'undefined') { // Add the specified Array methods to the Collection prototype. Object.defineProperties(realmConstructor.Collection.prototype, arrayMethods); +// TODO: Remove this now useless object. +Object.defineProperty(realmConstructor, 'Types', { + value: Object.freeze({ + 'BOOL': 'bool', + 'INT': 'int', + 'FLOAT': 'float', + 'DOUBLE': 'double', + 'STRING': 'string', + 'DATE': 'date', + 'DATA': 'data', + 'OBJECT': 'object', + 'LIST': 'list', + }) +}); + module.exports = realmConstructor; diff --git a/package.json b/package.json index 6839cfa7..f7e3a128 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "bindings": "^1.2.1", + "mockery": "^1.6.2", "nan": "^2.2.1", "node-gyp": "^3.3.1", "rnpm": "1.5.2", diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 536a75fd..7662ccf9 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -50,11 +50,10 @@ F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; }; F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; }; F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; }; - F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */; }; F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; }; F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */; }; F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; }; - F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.c */; }; + F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; }; F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; }; F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; }; F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; }; @@ -174,7 +173,6 @@ 02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier.hpp; path = src/impl/weak_realm_notifier.hpp; sourceTree = ""; }; F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = ""; }; F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = ""; }; - F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_object_accessor.cpp; sourceTree = ""; }; F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = ""; }; F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = ""; }; F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = ""; }; @@ -187,7 +185,7 @@ F620F0591CB7B4C80082977B /* js_object_accessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_object_accessor.hpp; sourceTree = ""; }; F620F0741CB9F60C0082977B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; }; F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = ""; }; - F6267BCA1CADC49200AC36B1 /* node_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node_dummy.c; sourceTree = ""; }; + F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_dummy.cpp; sourceTree = ""; }; F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; }; F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RealmJS.mm; sourceTree = ""; }; @@ -410,13 +408,12 @@ F62BF9001CAC72C40022BCDC /* Node */ = { isa = PBXGroup; children = ( - F6267BCA1CADC49200AC36B1 /* node_dummy.c */, + F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */, F60102CF1CBB814A00EC01BA /* node_init.hpp */, F620F0571CB766DA0082977B /* node_init.cpp */, F620F0551CB655A50082977B /* node_class.hpp */, F6874A351CAC792D00EEEE36 /* node_types.hpp */, F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */, - F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */, ); name = Node; path = node; @@ -707,7 +704,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cp ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\""; + shellScript = "cp -f ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\""; }; F63FF2C51C12462600B3B8E0 /* Download Core */ = { isa = PBXShellScriptBuildPhase; @@ -741,7 +738,6 @@ buildActionMask = 2147483647; files = ( F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */, - F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */, F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */, F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */, F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */, @@ -752,7 +748,7 @@ F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */, F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */, F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */, - F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */, + F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */, F60102D81CBB96BD00EC01BA /* results.cpp in Sources */, F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */, F620F0581CB766DA0082977B /* node_init.cpp in Sources */, diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 4233d4af..2ea8ebf3 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -18,8 +18,8 @@ #pragma once +#include #include -#include #include "js_class.hpp" #include "js_types.hpp" @@ -67,10 +67,20 @@ class RealmDelegate : public BindingContext { } void add_notification(TFunction notification) { - m_notifications.insert(Protected(m_context, notification)); + for (auto &handler : m_notifications) { + if (handler == notification) { + return; + } + } + m_notifications.emplace_back(m_context, notification); } void remove_notification(TFunction notification) { - m_notifications.erase(Protected(m_context, notification)); + for (auto iter = m_notifications.begin(); iter != m_notifications.end(); ++iter) { + if (*iter == notification) { + m_notifications.erase(iter); + return; + } + } } void remove_all_notifications() { m_notifications.clear(); @@ -81,7 +91,7 @@ class RealmDelegate : public BindingContext { private: Protected m_context; - std::set> m_notifications; + std::list> m_notifications; std::weak_ptr m_realm; void notify(const char *notification_name) { @@ -142,11 +152,11 @@ class Realm { static void GetDefaultPath(TContext, TObject, ReturnValue &); static void SetDefaultPath(TContext, TObject, TValue value); - static TObject create_constructor(TContext ctx) { - TObject realm_constructor = ObjectWrap>::create_constructor(ctx); - TObject collection_constructor = ObjectWrap>::create_constructor(ctx); - TObject list_constructor = ObjectWrap>::create_constructor(ctx); - TObject results_constructor = ObjectWrap>::create_constructor(ctx); + static TFunction create_constructor(TContext ctx) { + TFunction realm_constructor = ObjectWrap>::create_constructor(ctx); + TFunction collection_constructor = ObjectWrap>::create_constructor(ctx); + TFunction list_constructor = ObjectWrap>::create_constructor(ctx); + TFunction results_constructor = ObjectWrap>::create_constructor(ctx); PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); diff --git a/src/js_types.hpp b/src/js_types.hpp index a9697efc..3bd81bf2 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -73,6 +73,7 @@ struct Value { static bool is_array(TContext, const TValue &); static bool is_array_buffer(TContext, const TValue &); + static bool is_array_buffer_view(TContext, const TValue &); static bool is_boolean(TContext, const TValue &); static bool is_constructor(TContext, const TValue &); static bool is_date(TContext, const TValue &); @@ -239,7 +240,6 @@ class Protected { bool operator!=(const TValue &) const; bool operator==(const Protected &) const; bool operator!=(const Protected &) const; - bool operator<(const Protected &) const; }; template @@ -249,6 +249,8 @@ struct Exception : public std::runtime_error { const Protected m_value; + Exception(TContext ctx, const std::string &message) + : std::runtime_error(message), m_value(value(ctx, message)) {} Exception(TContext ctx, const TValue &val) : std::runtime_error(std::string(Value::to_string(ctx, val))), m_value(ctx, val) {} diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 3299fc5c..2aefe1cf 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -40,30 +40,32 @@ template class ObjectWrap { public: using Internal = typename ClassType::Internal; + operator Internal*() const { return m_object.get(); } + ObjectWrap& operator=(Internal* object) { if (m_object.get() != object) { m_object = std::unique_ptr(object); } return *this; } - + static JSClassRef get_class() { static JSClassRef js_class = create_class(); return js_class; } - + static JSClassRef get_constructor_class() { static JSClassRef js_class = create_constructor_class(); return js_class; } - + static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); } - + static JSObjectRef create_constructor(JSContextRef ctx) { if (JSClassRef constructor_class = get_constructor_class()) { return JSObjectMake(ctx, constructor_class, nullptr); @@ -71,11 +73,11 @@ public: return JSObjectMakeConstructor(ctx, get_class(), construct); } - + static bool has_instance(JSContextRef ctx, JSValueRef value) { return JSValueIsObjectOfClass(ctx, value, get_class()); } - + private: static ClassType s_class; @@ -134,7 +136,7 @@ private: return index_setter(ctx, object, index, value, exception); } else { - *exception = Exception::value(ctx, std::string("Cannot assigned to read only index ") + util::to_string(index)); + *exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index)); return false; } } @@ -288,33 +290,9 @@ class ObjectWrap { public: using Internal = void; - operator Internal*() const { - return nullptr; - } - - ObjectWrap& operator=(Internal* object) { - return *this; - } - static JSClassRef get_class() { return nullptr; } - - static JSClassRef get_constructor_class() { - return nullptr; - } - - static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { - return nullptr; - } - - static JSObjectRef create_constructor(JSContextRef ctx) { - return nullptr; - } - - static bool has_instance(JSContextRef ctx, JSValueRef value) { - return nullptr; - } }; // The declared static variables must be defined as well. diff --git a/src/jsc/jsc_init.cpp b/src/jsc/jsc_init.cpp index 3a36ba6a..cb78f7c7 100644 --- a/src/jsc/jsc_init.cpp +++ b/src/jsc/jsc_init.cpp @@ -27,39 +27,8 @@ extern "C" { using namespace realm; using namespace realm::jsc; -JSValueRef RJSTypeGet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { - std::string str = String(propertyName); - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - return Value::from_string(ctx, str); -} - -JSClassRef RJSRealmTypeClass() { - JSClassDefinition realmTypesDefinition = kJSClassDefinitionEmpty; - realmTypesDefinition.className = "PropTypes"; - JSStaticValue types[] = { - { "BOOL", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "INT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "FLOAT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "DOUBLE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "STRING", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "DATE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "DATA", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "OBJECT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { NULL, NULL, NULL, 0 } - }; - realmTypesDefinition.staticValues = types; - return JSClassCreate(&realmTypesDefinition); -} - JSObjectRef RJSConstructorCreate(JSContextRef ctx) { - JSObjectRef realm_constructor = js::Realm::create_constructor(ctx); - JSObjectRef types_object = JSObjectMake(ctx, RJSRealmTypeClass(), nullptr); - - // TODO: Either remove this (preferable) or move implementation to JS. - jsc::Object::set_property(ctx, realm_constructor, "Types", types_object, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete)); - - return realm_constructor; + return js::Realm::create_constructor(ctx); } void RJSInitializeInContext(JSContextRef ctx) { diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index 039b9c27..835f0f96 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -57,21 +57,6 @@ class Protected { operator T() const { return m_value; } - bool operator==(const T &other) const { - return m_value == other; - } - bool operator!=(const T &other) const { - return m_value != other; - } - bool operator==(const Protected &other) const { - return m_value == other; - } - bool operator!=(const Protected &other) const { - return m_value != other; - } - bool operator<(const Protected &other) const { - return m_value < other.m_value; - } }; template @@ -126,7 +111,7 @@ class ReturnValue { const JSContextRef m_context; JSValueRef m_value = nullptr; -public: + public: ReturnValue(JSContextRef ctx) : m_context(ctx) {} void set(const JSValueRef &value) { diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 27e93d81..5319e011 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -20,6 +20,7 @@ #include "node_types.hpp" #include "js_class.hpp" +#include "js_util.hpp" namespace realm { namespace node { @@ -45,9 +46,15 @@ static inline std::vector> get_arguments(const Nan::Functio return arguments; } -static inline void setup_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { - Nan::HandleScope scope; +static inline void setup_static_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { + v8::Local fn = Nan::GetFunction(Nan::New(callback)).ToLocalChecked(); + v8::Local fn_name = Nan::New(name).ToLocalChecked(); + tpl->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); + fn->SetName(fn_name); +} + +static inline void setup_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { v8::Local signature = Nan::New(tpl); v8::Local t = Nan::New(callback, v8::Local(), signature); v8::Local fn = Nan::GetFunction(t).ToLocalChecked(); @@ -58,54 +65,83 @@ static inline void setup_method(v8::Local tpl, const std:: fn->SetName(fn_name); } -static inline void setup_property(v8::Local tpl, const std::string &name, const PropertyType &property) { - Nan::HandleScope scope; - - v8::Local prop_name = Nan::New(name).ToLocalChecked(); - v8::PropertyAttribute attributes = static_cast(v8::DontEnum | v8::DontDelete | (property.setter ? v8::None : v8::ReadOnly)); - - Nan::SetAccessor(tpl, prop_name, property.getter, property.setter, v8::Local(), v8::DEFAULT, attributes); +static inline void set_readonly_property(v8::Local property, v8::Local value, Nan::NAN_SETTER_ARGS_TYPE info) { + std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'"; + Nan::ThrowError(message.c_str()); } -template +static inline void set_readonly_index(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { + std::string message = std::string("Cannot assign to read only index ") + util::to_string(index); + Nan::ThrowError(message.c_str()); +} + +template +static inline void setup_property(v8::Local target, const std::string &name, const PropertyType &property) { + v8::Local prop_name = Nan::New(name).ToLocalChecked(); + v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete); + + Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local(), v8::DEFAULT, attributes); +} + +template class ObjectWrap : public Nan::ObjectWrap { - static ClassDefinition s_class; - static Nan::Persistent s_constructor; - static Nan::Persistent s_template; + using Internal = typename ClassType::Internal; - std::unique_ptr m_object; + static ClassType s_class; - ObjectWrap(T* object = nullptr) : m_object(object) {} + std::unique_ptr m_object; - public: - operator T*() const { - return m_object.get(); + ObjectWrap(Internal* object = nullptr) : m_object(object) {} + + template + static v8::Local get_superclass(ClassDefinition*) { + return ObjectWrap::get_template(); } - ObjectWrap& operator=(T* object) { - if (m_object.get() != object) { - m_object = std::unique_ptr(object); + + static void get_nonexistent_property(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) { + // Do nothing. This function exists only to prevent a crash where it is used. + } + + static void set_property(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { + if (s_class.index_accessor.getter || s_class.index_accessor.setter) { + try { + // Negative indices are passed into this string property interceptor, so check for them here. + validated_positive_index(node::String(property)); + } + catch (std::out_of_range &e) { + Nan::ThrowError(Exception::value(info.GetIsolate(), e)); + return; + } + catch (std::invalid_argument &) { + // Property is not a number. + } + } + if (auto string_setter = s_class.string_accessor.setter) { + string_setter(property, value, info); } - return *this; } - static v8::Local create_instance(v8::Isolate* isolate, T* internal = nullptr) { + static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) { + uint32_t length; + try { + length = Object::validated_get_length(info.GetIsolate(), info.This()); + } + catch (std::exception &) { + // Enumerating properties should never throw an exception. + return; + } + + v8::Local array = Nan::New(length); + for (uint32_t i = 0; i < length; i++) { + Nan::Set(array, i, Nan::New(i)); + } + + info.GetReturnValue().Set(array); + } + + static v8::Local create_template() { Nan::EscapableHandleScope scope; - // TODO: Figure out why this template ends up being empty here. - v8::Local tpl = Nan::New(s_template); - v8::Local instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked(); - - auto wrap = new ObjectWrap(internal); - wrap->Wrap(instance); - - return scope.Escape(instance); - } - - static bool has_instance(v8::Isolate* isolate, const v8::Local &value) { - return Nan::New(s_template)->HasInstance(value); - } - - static NAN_MODULE_INIT(init) { v8::Local tpl = Nan::New(construct); v8::Local instance_tpl = tpl->InstanceTemplate(); v8::Local name = Nan::New(s_class.name).ToLocalChecked(); @@ -113,32 +149,81 @@ class ObjectWrap : public Nan::ObjectWrap { tpl->SetClassName(name); instance_tpl->SetInternalFieldCount(1); - // TODO: Setup static properties and methods. + v8::Local super_tpl = get_superclass(s_class.superclass); + if (!super_tpl.IsEmpty()) { + tpl->Inherit(super_tpl); + } + + // Static properties are setup in create_constructor() because V8. + for (auto &pair : s_class.static_methods) { + setup_static_method(tpl, pair.first, pair.second); + } for (auto &pair : s_class.methods) { setup_method(tpl, pair.first, pair.second); } for (auto &pair : s_class.properties) { - setup_property(instance_tpl, pair.first, pair.second); + setup_property(instance_tpl, pair.first, pair.second); } if (s_class.index_accessor.getter) { - // TODO: Add our own index enumerator. auto &index_accessor = s_class.index_accessor; - Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter); + Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes); } - if (s_class.string_accessor.getter) { + if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) { + // Use our own wrapper for the setter since we want to throw for negative indices. auto &string_accessor = s_class.string_accessor; - Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter, string_accessor.setter, 0, 0, string_accessor.enumerator); + Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator); } - v8::Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); - s_constructor.Reset(constructor); - s_template.Reset(tpl); - - Nan::Set(target, name, constructor); + return scope.Escape(tpl); } - static NAN_METHOD(construct) { + public: + operator Internal*() const { + return m_object.get(); + } + ObjectWrap& operator=(Internal* object) { + if (m_object.get() != object) { + m_object = std::unique_ptr(object); + } + return *this; + } + + static v8::Local get_template() { + static Nan::Persistent js_template(create_template()); + return Nan::New(js_template); + } + + static v8::Local create_constructor(v8::Isolate* isolate) { + Nan::EscapableHandleScope scope; + + v8::Local tpl = get_template(); + v8::Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); + + for (auto &pair : s_class.static_properties) { + setup_property(constructor, pair.first, pair.second); + } + + return scope.Escape(constructor); + } + + static v8::Local create_instance(v8::Isolate* isolate, Internal* internal = nullptr) { + Nan::EscapableHandleScope scope; + + v8::Local tpl = get_template(); + v8::Local instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked(); + + auto wrap = new ObjectWrap(internal); + wrap->Wrap(instance); + + return scope.Escape(instance); + } + + static bool has_instance(v8::Isolate* isolate, const v8::Local &value) { + return get_template()->HasInstance(value); + } + + static void construct(Nan::NAN_METHOD_ARGS_TYPE info) { if (!info.IsConstructCall()) { Nan::ThrowError("Constructor must be called with new"); } @@ -148,7 +233,7 @@ class ObjectWrap : public Nan::ObjectWrap { v8::Local this_object = info.This(); info.GetReturnValue().Set(this_object); - auto wrap = new ObjectWrap(); + auto wrap = new ObjectWrap(); wrap->Wrap(this_object); try { @@ -164,10 +249,19 @@ class ObjectWrap : public Nan::ObjectWrap { } }; +template<> +class ObjectWrap { + public: + using Internal = void; + + static v8::Local get_template() { + return v8::Local();; + } +}; + // The declared static variables must be defined as well. -template ClassDefinition ObjectWrap::s_class; -template Nan::Persistent ObjectWrap::s_constructor; -template Nan::Persistent ObjectWrap::s_template; +template +ClassType ObjectWrap::s_class; } // node @@ -190,7 +284,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { } } -Type::template +template void wrap(v8::Local property, Nan::NAN_GETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); node::ReturnValue return_value(info.GetReturnValue()); @@ -220,6 +314,10 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) { try { F(isolate, info.This(), index, return_value); } + catch (std::out_of_range &) { + // Out-of-bounds index getters should just return undefined in JS. + return_value.set_undefined(); + } catch(std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } @@ -229,7 +327,10 @@ template void wrap(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); try { - F(isolate, info.This(), index, value); + if (F(isolate, info.This(), index, value)) { + // Indicate that the property was intercepted. + info.GetReturnValue().Set(value); + } } catch(std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); @@ -252,7 +353,10 @@ template void wrap(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { v8::Isolate* isolate = info.GetIsolate(); try { - F(isolate, info.This(), property, value); + if (F(isolate, info.This(), property, value)) { + // Indicate that the property was intercepted. + info.GetReturnValue().Set(value); + } } catch(std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); diff --git a/src/node/node_dummy.c b/src/node/node_dummy.cpp similarity index 65% rename from src/node/node_dummy.c rename to src/node/node_dummy.cpp index 1221fe3b..c4522d55 100644 --- a/src/node/node_dummy.c +++ b/src/node/node_dummy.cpp @@ -16,4 +16,15 @@ // //////////////////////////////////////////////////////////////////////////// -void node_module_register(void* mod) {} +// NOTE: This dummy file exists only to make Xcode build the Realm Node dynamic library. +#include "node.h" + +extern "C" void node_module_register(void* mod) {} + +namespace node { +namespace Buffer { + bool HasInstance(v8::Local val) { return false; } + char* Data(v8::Local val) { return nullptr; } + size_t Length(v8::Local val) { return 0; } +} +} diff --git a/src/node/node_init.cpp b/src/node/node_init.cpp index d9bf5cfa..da21c3ff 100644 --- a/src/node/node_init.cpp +++ b/src/node/node_init.cpp @@ -22,13 +22,10 @@ namespace realm { namespace node { static void init(v8::Local exports) { - ObjectWrap::init(exports); - ObjectWrap::init(exports); -// RealmObjectWrap::Init(exports); -// RealmResultsWrap::Init(exports); -// RealmListWrap::Init(exports); -// -// NODE_SET_METHOD(exports, "__clearCaches", ClearCaches); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Local realm_constructor = js::Realm::create_constructor(isolate); + + Nan::Set(exports, realm_constructor->GetName(), realm_constructor); } } // node diff --git a/src/node/node_object_accessor.cpp b/src/node/node_object_accessor.cpp deleted file mode 100644 index 3c244331..00000000 --- a/src/node/node_object_accessor.cpp +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "node_object_accessor.hpp" - -using namespace realm; -using namespace realm::node; - -using Accessor = js::NativeAccessor; - -template<> -std::string Accessor::to_binary(v8::Isolate* isolate, v8::Local &value) { - // TODO - return std::string(); -} - -template<> -v8::Local Accessor::from_binary(v8::Isolate* isolate, BinaryData data) { - // TODO - return v8::Local(); -} diff --git a/src/node/node_object_accessor.hpp b/src/node/node_object_accessor.hpp index 975aab37..029ffdd0 100644 --- a/src/node/node_object_accessor.hpp +++ b/src/node/node_object_accessor.hpp @@ -27,4 +27,49 @@ namespace realm { template<> class NativeAccessor : public js::NativeAccessor {}; +namespace js { + +template<> +inline std::string NativeAccessor::to_binary(v8::Isolate* isolate, v8::Local &value) { + if (Value::is_array_buffer(isolate, value)) { + // TODO: This probably needs some abstraction for older V8. +#if REALM_V8_ARRAY_BUFFER_API + v8::Local array_buffer = value.As(); + v8::ArrayBuffer::Contents contents = array_buffer->GetContents(); + + return std::string(static_cast(contents.Data()), contents.ByteLength()); +#else + // TODO: Implement this for older V8 +#endif + } + else if (Value::is_array_buffer_view(isolate, value)) { + Nan::TypedArrayContents contents(value); + + return std::string(*contents, contents.length()); + } + else if (::node::Buffer::HasInstance(value)) { + return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value)); + } + + throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary"); +} + +template<> +inline v8::Local NativeAccessor::from_binary(v8::Isolate* isolate, BinaryData data) { +#if REALM_V8_ARRAY_BUFFER_API + size_t byte_count = data.size(); + void* bytes = nullptr; + + if (byte_count) { + bytes = memcpy(malloc(byte_count), data.data(), byte_count); + } + + // An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected. + return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized); +#else + // TODO: Implement this for older V8 +#endif +} + +} // js } // realm diff --git a/src/node/node_types.hpp b/src/node/node_types.hpp index 95643197..d99343ac 100644 --- a/src/node/node_types.hpp +++ b/src/node/node_types.hpp @@ -27,6 +27,10 @@ #include "js_types.hpp" +#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || (V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3)) +#define REALM_V8_ARRAY_BUFFER_API 1 +#endif + namespace realm { namespace node { @@ -72,9 +76,6 @@ class Protected { bool operator!=(const Protected &other) const { return m_value != other.m_value; } - bool operator<(const Protected &other) const { - return *Nan::New(m_value) < *Nan::New(other.m_value); - } }; template @@ -188,7 +189,20 @@ inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local &value) { +#if REALM_V8_ARRAY_BUFFER_API return value->IsArrayBuffer(); +#else + // TODO: Implement this! +#endif +} + +template<> +inline bool node::Value::is_array_buffer_view(v8::Isolate* isolate, const v8::Local &value) { +#if REALM_V8_ARRAY_BUFFER_API + return value->IsArrayBufferView(); +#else + // TODO: Implement this! +#endif } template<> @@ -440,27 +454,27 @@ inline v8::Local node::Object::create_date(v8::Isolate* isolate, dou } template<> -template -inline v8::Local node::Object::create_instance(v8::Isolate* isolate, U* internal) { - return node::ObjectWrap::create_instance(isolate, internal); +template +inline v8::Local node::Object::create_instance(v8::Isolate* isolate, typename ClassType::Internal* internal) { + return node::ObjectWrap::create_instance(isolate, internal); } template<> -template +template inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local &object) { - return node::ObjectWrap::has_instance(isolate, object); + return node::ObjectWrap::has_instance(isolate, object); } template<> -template -inline U* node::Object::get_internal(const v8::Local &object) { - return *Nan::ObjectWrap::Unwrap>(object); +template +inline typename ClassType::Internal* node::Object::get_internal(const v8::Local &object) { + return *Nan::ObjectWrap::Unwrap>(object); } template<> -template -inline void node::Object::set_internal(const v8::Local &object, U* ptr) { - auto wrap = Nan::ObjectWrap::Unwrap>(object); +template +inline void node::Object::set_internal(const v8::Local &object, typename ClassType::Internal* ptr) { + auto wrap = Nan::ObjectWrap::Unwrap>(object); *wrap = ptr; } diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 00000000..48455ea8 --- /dev/null +++ b/tests/index.js @@ -0,0 +1,38 @@ +'use strict'; + +const mockery = require('mockery'); + +function runTests() { + const RealmTests = require('./js'); + const testNames = RealmTests.getTestNames(); + + for (let suiteName in testNames) { + let testSuite = RealmTests[suiteName]; + + console.log('Starting ' + suiteName); + + for (let testName of testNames[suiteName]) { + RealmTests.runTest(suiteName, 'beforeEach'); + + try { + RealmTests.runTest(suiteName, testName); + console.log('+ ' + testName); + } + catch (e) { + console.warn('- ' + testName); + console.error(e.message, e.stack); + } + finally { + RealmTests.runTest(suiteName, 'afterEach'); + } + } + } +} + +if (require.main == module) { + mockery.enable(); + mockery.warnOnUnregistered(false); + mockery.registerMock('realm', require('..')); + + runTests(); +} diff --git a/tests/js/query-tests.json b/tests/js/query-tests.json new file mode 120000 index 00000000..cb35905f --- /dev/null +++ b/tests/js/query-tests.json @@ -0,0 +1 @@ +../../src/object-store/tests/query.json \ No newline at end of file From 2641e5a2a1128d44a8b2d13944dfeb33ac57696a Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Fri, 15 Apr 2016 16:10:25 -0700 Subject: [PATCH 23/48] Move JSC object accessor code into header --- src/ios/RealmJS.xcodeproj/project.pbxproj | 4 - src/jsc/jsc_object_accessor.cpp | 90 ----------------------- src/jsc/jsc_object_accessor.hpp | 69 +++++++++++++++++ 3 files changed, 69 insertions(+), 94 deletions(-) delete mode 100644 src/jsc/jsc_object_accessor.cpp diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 7662ccf9..a3a86c77 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -51,7 +51,6 @@ F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; }; F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; }; F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; }; - F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */; }; F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; }; F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; }; F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; }; @@ -174,7 +173,6 @@ F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = ""; }; F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = ""; }; F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = ""; }; - F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = ""; }; F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = ""; }; F60102F71CBDA6D400EC01BA /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = ""; }; F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; @@ -488,7 +486,6 @@ F620F0531CAF2EF70082977B /* jsc_class.hpp */, 025678951CAB392000FB8501 /* jsc_types.hpp */, F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */, - F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */, ); name = JSC; path = jsc; @@ -776,7 +773,6 @@ 02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */, F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */, 02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */, - F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */, 02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */, 02F59EC41C88F17D007F774C /* schema.cpp in Sources */, F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */, diff --git a/src/jsc/jsc_object_accessor.cpp b/src/jsc/jsc_object_accessor.cpp deleted file mode 100644 index 99c46832..00000000 --- a/src/jsc/jsc_object_accessor.cpp +++ /dev/null @@ -1,90 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "jsc_object_accessor.hpp" - -using namespace realm; -using namespace realm::jsc; - -using Accessor = js::NativeAccessor; - -template<> -std::string Accessor::to_binary(JSContextRef ctx, JSValueRef &value) { - static jsc::String s_array_buffer = "ArrayBuffer"; - static jsc::String s_buffer = "buffer"; - static jsc::String s_byte_length = "byteLength"; - static jsc::String s_byte_offset = "byteOffset"; - static jsc::String s_is_view = "isView"; - static jsc::String s_uint8_array = "Uint8Array"; - - JSObjectRef global_object = JSContextGetGlobalObject(ctx); - JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer); - JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_uint8_array); - JSValueRef uint8_array_arguments[3]; - uint32_t uint8_array_argc = 0; - - // Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView). - if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) { - uint8_array_arguments[0] = value; - uint8_array_argc = 1; - } - else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) { - // Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val). - JSValueRef is_view = jsc::Object::call_method(ctx, array_buffer_constructor, s_is_view, 1, &object); - - if (jsc::Value::to_boolean(ctx, is_view)) { - uint8_array_arguments[0] = jsc::Object::validated_get_object(ctx, object, s_buffer); - uint8_array_arguments[1] = jsc::Object::get_property(ctx, object, s_byte_offset); - uint8_array_arguments[2] = jsc::Object::get_property(ctx, object, s_byte_length); - uint8_array_argc = 3; - } - } - - if (!uint8_array_argc) { - throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary"); - } - - JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments); - uint32_t byte_count = jsc::Object::validated_get_length(ctx, uint8_array); - std::string bytes(byte_count, 0); - - for (uint32_t i = 0; i < byte_count; i++) { - JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i); - bytes[i] = jsc::Value::to_number(ctx, byteValue); - } - - return bytes; -} - -template<> -JSValueRef Accessor::from_binary(JSContextRef ctx, BinaryData data) { - static jsc::String s_buffer = "buffer"; - static jsc::String s_uint8_array = "Uint8Array"; - - size_t byte_count = data.size(); - JSValueRef byte_count_value = jsc::Value::from_number(ctx, byte_count); - JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, JSContextGetGlobalObject(ctx), s_uint8_array); - JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, 1, &byte_count_value); - - for (uint32_t i = 0; i < byte_count; i++) { - JSValueRef num = jsc::Value::from_number(ctx, data[i]); - jsc::Object::set_property(ctx, uint8_array, i, num); - } - - return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer); -} diff --git a/src/jsc/jsc_object_accessor.hpp b/src/jsc/jsc_object_accessor.hpp index b93e6d9e..d210d90a 100644 --- a/src/jsc/jsc_object_accessor.hpp +++ b/src/jsc/jsc_object_accessor.hpp @@ -26,5 +26,74 @@ namespace realm { // Specialize a native accessor class for JSC. template<> class NativeAccessor : public js::NativeAccessor {}; + +namespace js { + +template<> +inline std::string NativeAccessor::to_binary(JSContextRef ctx, JSValueRef &value) { + static jsc::String s_array_buffer = "ArrayBuffer"; + static jsc::String s_buffer = "buffer"; + static jsc::String s_byte_length = "byteLength"; + static jsc::String s_byte_offset = "byteOffset"; + static jsc::String s_is_view = "isView"; + static jsc::String s_uint8_array = "Uint8Array"; + + JSObjectRef global_object = JSContextGetGlobalObject(ctx); + JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_uint8_array); + JSValueRef uint8_array_arguments[3]; + uint32_t uint8_array_argc = 0; + + // Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView). + if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) { + uint8_array_arguments[0] = value; + uint8_array_argc = 1; + } + else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) { + // Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val). + JSValueRef is_view = jsc::Object::call_method(ctx, array_buffer_constructor, s_is_view, 1, &object); + + if (jsc::Value::to_boolean(ctx, is_view)) { + uint8_array_arguments[0] = jsc::Object::validated_get_object(ctx, object, s_buffer); + uint8_array_arguments[1] = jsc::Object::get_property(ctx, object, s_byte_offset); + uint8_array_arguments[2] = jsc::Object::get_property(ctx, object, s_byte_length); + uint8_array_argc = 3; + } + } + + if (!uint8_array_argc) { + throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary"); + } + + JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments); + uint32_t byte_count = jsc::Object::validated_get_length(ctx, uint8_array); + std::string bytes(byte_count, 0); + + for (uint32_t i = 0; i < byte_count; i++) { + JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i); + bytes[i] = jsc::Value::to_number(ctx, byteValue); + } + + return bytes; +} + +template<> +inline JSValueRef NativeAccessor::from_binary(JSContextRef ctx, BinaryData data) { + static jsc::String s_buffer = "buffer"; + static jsc::String s_uint8_array = "Uint8Array"; + + size_t byte_count = data.size(); + JSValueRef byte_count_value = jsc::Value::from_number(ctx, byte_count); + JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, JSContextGetGlobalObject(ctx), s_uint8_array); + JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, 1, &byte_count_value); + + for (uint32_t i = 0; i < byte_count; i++) { + JSValueRef num = jsc::Value::from_number(ctx, data[i]); + jsc::Object::set_property(ctx, uint8_array, i, num); + } + return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer); +} + +} // js } // realm From 999900ff00587ca29f5932615c4a493e624ce17c Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sat, 16 Apr 2016 16:30:02 -0700 Subject: [PATCH 24/48] Fix script that copies query-tests.json --- src/ios/RealmJS.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index a3a86c77..6c1bc8db 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -701,7 +701,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cp -f ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\""; + shellScript = "DEST=\"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"\nrm -f \"$DEST\"\ncp ../object-store/tests/query.json \"$DEST\""; }; F63FF2C51C12462600B3B8E0 /* Download Core */ = { isa = PBXShellScriptBuildPhase; From 7c97a1752eda4ae34bf83053b2a0d5316a4b5e42 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sat, 16 Apr 2016 16:46:16 -0700 Subject: [PATCH 25/48] Remove need for BaseClassDefinition --- src/js_class.hpp | 27 +++++++++++---------------- src/js_collection.hpp | 2 +- src/js_list.hpp | 2 +- src/js_object.hpp | 2 +- src/js_realm.hpp | 2 +- src/js_results.hpp | 2 +- src/jsc/jsc_class.hpp | 8 ++------ src/node/node_class.hpp | 8 ++------ 8 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/js_class.hpp b/src/js_class.hpp index 0e6eb112..39603897 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -68,27 +68,22 @@ using MethodMap = std::map; template using PropertyMap = std::map>; -template +template struct ClassDefinition { using Internal = U; + using Parent = V; - // Every specialization *must* at least have a name. - std::string name; -}; - -template -struct BaseClassDefinition { - // This pointer does not need to be set. - ClassDefinition* superclass; + // Every subclass *must* at least have a name. + // std::string const name; // ClassDefinition specializations should inherit from this class and override what's needed below. - ConstructorType* constructor; - MethodMap static_methods; - PropertyMap static_properties; - MethodMap methods; - PropertyMap properties; - IndexPropertyType index_accessor; - StringPropertyType string_accessor; + ConstructorType* const constructor = nullptr; + MethodMap const static_methods = {}; + PropertyMap const static_properties = {}; + MethodMap const methods = {}; + PropertyMap const properties = {}; + IndexPropertyType const index_accessor = {}; + StringPropertyType const string_accessor = {}; }; template diff --git a/src/js_collection.hpp b/src/js_collection.hpp index 2ed53915..510333a7 100644 --- a/src/js_collection.hpp +++ b/src/js_collection.hpp @@ -27,7 +27,7 @@ namespace js { class Collection {}; template -struct CollectionClass : ClassDefinition, BaseClassDefinition { +struct CollectionClass : ClassDefinition { std::string const name = "Collection"; }; diff --git a/src/js_list.hpp b/src/js_list.hpp index a1152915..b78883c7 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -58,7 +58,7 @@ struct List { }; template -struct ListClass : ClassDefinition, BaseClassDefinition> { +struct ListClass : ClassDefinition> { using List = List; std::string const name = "List"; diff --git a/src/js_object.hpp b/src/js_object.hpp index f79ddbce..9872706b 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -48,7 +48,7 @@ struct RealmObject { }; template -struct RealmObjectClass : ClassDefinition, BaseClassDefinition { +struct RealmObjectClass : ClassDefinition { using RealmObject = RealmObject; const std::string name = "RealmObject"; diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 2ea8ebf3..b7609391 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -199,7 +199,7 @@ class Realm { }; template -struct RealmClass : ClassDefinition, BaseClassDefinition { +struct RealmClass : ClassDefinition { using Realm = Realm; std::string const name = "Realm"; diff --git a/src/js_results.hpp b/src/js_results.hpp index a0577b3a..641293f9 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -58,7 +58,7 @@ struct Results { }; template -struct ResultsClass : ClassDefinition, BaseClassDefinition> { +struct ResultsClass : ClassDefinition> { using Results = Results; std::string const name = "Results"; diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 2aefe1cf..403f37bf 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -40,6 +40,7 @@ template class ObjectWrap { public: using Internal = typename ClassType::Internal; + using ParentClassType = typename ClassType::Parent; operator Internal*() const { return m_object.get(); @@ -186,11 +187,6 @@ private: } } - template - static JSClassRef get_superclass(ClassDefinition*) { - return ObjectWrap::get_class(); - } - static std::vector get_methods(const MethodMap &methods) { std::vector functions; functions.reserve(methods.size() + 1); @@ -227,7 +223,7 @@ private: std::vector methods; std::vector properties; - definition.parentClass = get_superclass(s_class.superclass); + definition.parentClass = ObjectWrap::get_class(); definition.className = s_class.name.c_str(); definition.finalize = finalize; diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 5319e011..3c9dbbc0 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -86,6 +86,7 @@ static inline void setup_property(v8::Local target, const std::strin template class ObjectWrap : public Nan::ObjectWrap { using Internal = typename ClassType::Internal; + using ParentClassType = typename ClassType::Parent; static ClassType s_class; @@ -93,11 +94,6 @@ class ObjectWrap : public Nan::ObjectWrap { ObjectWrap(Internal* object = nullptr) : m_object(object) {} - template - static v8::Local get_superclass(ClassDefinition*) { - return ObjectWrap::get_template(); - } - static void get_nonexistent_property(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) { // Do nothing. This function exists only to prevent a crash where it is used. } @@ -149,7 +145,7 @@ class ObjectWrap : public Nan::ObjectWrap { tpl->SetClassName(name); instance_tpl->SetInternalFieldCount(1); - v8::Local super_tpl = get_superclass(s_class.superclass); + v8::Local super_tpl = ObjectWrap::get_template(); if (!super_tpl.IsEmpty()) { tpl->Inherit(super_tpl); } From 5ed2c3cf25810aa369cb3b126883af6a254b76fe Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sun, 17 Apr 2016 21:50:20 -0700 Subject: [PATCH 26/48] Define most of node::ObjectWrap separately --- src/node/node_class.hpp | 409 +++++++++++++++++++++------------------- 1 file changed, 218 insertions(+), 191 deletions(-) diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 3c9dbbc0..5c54e95e 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -34,150 +34,28 @@ using PropertyType = js::PropertyType; using IndexPropertyType = js::IndexPropertyType; using StringPropertyType = js::StringPropertyType; -static inline std::vector> get_arguments(const Nan::FunctionCallbackInfo &info) { - int count = info.Length(); - std::vector> arguments; - arguments.reserve(count); - - for (int i = 0; i < count; i++) { - arguments.push_back(info[i]); - } - - return arguments; -} - -static inline void setup_static_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { - v8::Local fn = Nan::GetFunction(Nan::New(callback)).ToLocalChecked(); - v8::Local fn_name = Nan::New(name).ToLocalChecked(); - - tpl->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); - fn->SetName(fn_name); -} - -static inline void setup_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { - v8::Local signature = Nan::New(tpl); - v8::Local t = Nan::New(callback, v8::Local(), signature); - v8::Local fn = Nan::GetFunction(t).ToLocalChecked(); - v8::Local fn_name = Nan::New(name).ToLocalChecked(); - - // The reason we use this rather than Nan::SetPrototypeMethod is DontEnum. - tpl->PrototypeTemplate()->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); - fn->SetName(fn_name); -} - -static inline void set_readonly_property(v8::Local property, v8::Local value, Nan::NAN_SETTER_ARGS_TYPE info) { - std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'"; - Nan::ThrowError(message.c_str()); -} - -static inline void set_readonly_index(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { - std::string message = std::string("Cannot assign to read only index ") + util::to_string(index); - Nan::ThrowError(message.c_str()); -} - -template -static inline void setup_property(v8::Local target, const std::string &name, const PropertyType &property) { - v8::Local prop_name = Nan::New(name).ToLocalChecked(); - v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete); - - Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local(), v8::DEFAULT, attributes); -} - template class ObjectWrap : public Nan::ObjectWrap { using Internal = typename ClassType::Internal; using ParentClassType = typename ClassType::Parent; - static ClassType s_class; - - std::unique_ptr m_object; - - ObjectWrap(Internal* object = nullptr) : m_object(object) {} - - static void get_nonexistent_property(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) { - // Do nothing. This function exists only to prevent a crash where it is used. - } - - static void set_property(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { - if (s_class.index_accessor.getter || s_class.index_accessor.setter) { - try { - // Negative indices are passed into this string property interceptor, so check for them here. - validated_positive_index(node::String(property)); - } - catch (std::out_of_range &e) { - Nan::ThrowError(Exception::value(info.GetIsolate(), e)); - return; - } - catch (std::invalid_argument &) { - // Property is not a number. - } - } - if (auto string_setter = s_class.string_accessor.setter) { - string_setter(property, value, info); - } - } - - static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) { - uint32_t length; - try { - length = Object::validated_get_length(info.GetIsolate(), info.This()); - } - catch (std::exception &) { - // Enumerating properties should never throw an exception. - return; - } - - v8::Local array = Nan::New(length); - for (uint32_t i = 0; i < length; i++) { - Nan::Set(array, i, Nan::New(i)); - } - - info.GetReturnValue().Set(array); - } - - static v8::Local create_template() { - Nan::EscapableHandleScope scope; - - v8::Local tpl = Nan::New(construct); - v8::Local instance_tpl = tpl->InstanceTemplate(); - v8::Local name = Nan::New(s_class.name).ToLocalChecked(); - - tpl->SetClassName(name); - instance_tpl->SetInternalFieldCount(1); - - v8::Local super_tpl = ObjectWrap::get_template(); - if (!super_tpl.IsEmpty()) { - tpl->Inherit(super_tpl); - } - - // Static properties are setup in create_constructor() because V8. - for (auto &pair : s_class.static_methods) { - setup_static_method(tpl, pair.first, pair.second); - } - for (auto &pair : s_class.methods) { - setup_method(tpl, pair.first, pair.second); - } - for (auto &pair : s_class.properties) { - setup_property(instance_tpl, pair.first, pair.second); - } - - if (s_class.index_accessor.getter) { - auto &index_accessor = s_class.index_accessor; - Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes); - } - if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) { - // Use our own wrapper for the setter since we want to throw for negative indices. - auto &string_accessor = s_class.string_accessor; - Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator); - } - - return scope.Escape(tpl); - } - public: + static v8::Local create_constructor(v8::Isolate*); + static v8::Local create_instance(v8::Isolate*, Internal* = nullptr); + + static v8::Local get_template() { + static Nan::Persistent js_template(create_template()); + return Nan::New(js_template); + } + + static bool has_instance(v8::Isolate* isolate, const v8::Local &value) { + return get_template()->HasInstance(value); + } + operator Internal*() const { return m_object.get(); } + ObjectWrap& operator=(Internal* object) { if (m_object.get() != object) { m_object = std::unique_ptr(object); @@ -185,63 +63,37 @@ class ObjectWrap : public Nan::ObjectWrap { return *this; } - static v8::Local get_template() { - static Nan::Persistent js_template(create_template()); - return Nan::New(js_template); + private: + static ClassType s_class; + + std::unique_ptr m_object; + + ObjectWrap(Internal* object = nullptr) : m_object(object) {} + + static v8::Local create_template(); + + static void setup_method(v8::Local, const std::string &, Nan::FunctionCallback); + static void setup_static_method(v8::Local, const std::string &, Nan::FunctionCallback); + + template + static void setup_property(v8::Local, const std::string &, const PropertyType &); + + static void construct(Nan::NAN_METHOD_ARGS_TYPE); + static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE); + static void set_property(v8::Local, v8::Local, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE); + + static void set_readonly_property(v8::Local property, v8::Local value, Nan::NAN_SETTER_ARGS_TYPE info) { + std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'"; + Nan::ThrowError(message.c_str()); } - static v8::Local create_constructor(v8::Isolate* isolate) { - Nan::EscapableHandleScope scope; - - v8::Local tpl = get_template(); - v8::Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); - - for (auto &pair : s_class.static_properties) { - setup_property(constructor, pair.first, pair.second); - } - - return scope.Escape(constructor); + static void set_readonly_index(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) { + std::string message = std::string("Cannot assign to read only index ") + util::to_string(index); + Nan::ThrowError(message.c_str()); } - static v8::Local create_instance(v8::Isolate* isolate, Internal* internal = nullptr) { - Nan::EscapableHandleScope scope; - - v8::Local tpl = get_template(); - v8::Local instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked(); - - auto wrap = new ObjectWrap(internal); - wrap->Wrap(instance); - - return scope.Escape(instance); - } - - static bool has_instance(v8::Isolate* isolate, const v8::Local &value) { - return get_template()->HasInstance(value); - } - - static void construct(Nan::NAN_METHOD_ARGS_TYPE info) { - if (!info.IsConstructCall()) { - Nan::ThrowError("Constructor must be called with new"); - } - if (s_class.constructor) { - auto isolate = info.GetIsolate(); - auto arguments = get_arguments(info); - v8::Local this_object = info.This(); - info.GetReturnValue().Set(this_object); - - auto wrap = new ObjectWrap(); - wrap->Wrap(this_object); - - try { - s_class.constructor(isolate, this_object, arguments.size(), arguments.data()); - } - catch(std::exception &e) { - Nan::ThrowError(node::Exception::value(isolate, e)); - } - } - else { - Nan::ThrowError("Illegal constructor"); - } + static void get_nonexistent_property(v8::Local, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE) { + // Do nothing. This function exists only to prevent a crash where it is used. } }; @@ -255,16 +107,191 @@ class ObjectWrap { } }; -// The declared static variables must be defined as well. +// This helper function is needed outside the scope of the ObjectWrap class as well. +static inline std::vector> get_arguments(const Nan::FunctionCallbackInfo &info) { + int count = info.Length(); + std::vector> arguments; + arguments.reserve(count); + + for (int i = 0; i < count; i++) { + arguments.push_back(info[i]); + } + + return arguments; +} + +// The static class variable must be defined as well. template ClassType ObjectWrap::s_class; +template +inline v8::Local ObjectWrap::create_constructor(v8::Isolate* isolate) { + Nan::EscapableHandleScope scope; + + v8::Local tpl = get_template(); + v8::Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); + + for (auto &pair : s_class.static_properties) { + setup_property(constructor, pair.first, pair.second); + } + + return scope.Escape(constructor); +} + +template +inline v8::Local ObjectWrap::create_instance(v8::Isolate* isolate, Internal* internal) { + Nan::EscapableHandleScope scope; + + v8::Local tpl = get_template(); + v8::Local instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked(); + + auto wrap = new ObjectWrap(internal); + wrap->Wrap(instance); + + return scope.Escape(instance); +} + +template +inline v8::Local ObjectWrap::create_template() { + Nan::EscapableHandleScope scope; + + v8::Local tpl = Nan::New(construct); + v8::Local instance_tpl = tpl->InstanceTemplate(); + v8::Local name = Nan::New(s_class.name).ToLocalChecked(); + + tpl->SetClassName(name); + instance_tpl->SetInternalFieldCount(1); + + v8::Local super_tpl = ObjectWrap::get_template(); + if (!super_tpl.IsEmpty()) { + tpl->Inherit(super_tpl); + } + + // Static properties are setup in create_constructor() because V8. + for (auto &pair : s_class.static_methods) { + setup_static_method(tpl, pair.first, pair.second); + } + for (auto &pair : s_class.methods) { + setup_method(tpl, pair.first, pair.second); + } + for (auto &pair : s_class.properties) { + setup_property(instance_tpl, pair.first, pair.second); + } + + if (s_class.index_accessor.getter) { + auto &index_accessor = s_class.index_accessor; + Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes); + } + if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) { + // Use our own wrapper for the setter since we want to throw for negative indices. + auto &string_accessor = s_class.string_accessor; + Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator); + } + + return scope.Escape(tpl); +} + +template +inline void ObjectWrap::setup_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { + v8::Local signature = Nan::New(tpl); + v8::Local t = Nan::New(callback, v8::Local(), signature); + v8::Local fn = Nan::GetFunction(t).ToLocalChecked(); + v8::Local fn_name = Nan::New(name).ToLocalChecked(); + + // The reason we use this rather than Nan::SetPrototypeMethod is DontEnum. + tpl->PrototypeTemplate()->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); + fn->SetName(fn_name); +} + +template +inline void ObjectWrap::setup_static_method(v8::Local tpl, const std::string &name, Nan::FunctionCallback callback) { + v8::Local fn = Nan::GetFunction(Nan::New(callback)).ToLocalChecked(); + v8::Local fn_name = Nan::New(name).ToLocalChecked(); + + tpl->Set(fn_name, fn, v8::PropertyAttribute::DontEnum); + fn->SetName(fn_name); +} + +template +template +inline void ObjectWrap::setup_property(v8::Local target, const std::string &name, const PropertyType &property) { + v8::Local prop_name = Nan::New(name).ToLocalChecked(); + v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete); + + Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local(), v8::DEFAULT, attributes); +} + +template +inline void ObjectWrap::construct(Nan::NAN_METHOD_ARGS_TYPE info) { + if (!info.IsConstructCall()) { + Nan::ThrowError("Constructor must be called with new"); + } + if (s_class.constructor) { + auto isolate = info.GetIsolate(); + auto arguments = get_arguments(info); + v8::Local this_object = info.This(); + info.GetReturnValue().Set(this_object); + + auto wrap = new ObjectWrap(); + wrap->Wrap(this_object); + + try { + s_class.constructor(isolate, this_object, arguments.size(), arguments.data()); + } + catch(std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } + } + else { + Nan::ThrowError("Illegal constructor"); + } +} + +template +inline void ObjectWrap::get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) { + uint32_t length; + try { + length = Object::validated_get_length(info.GetIsolate(), info.This()); + } + catch (std::exception &) { + // Enumerating properties should never throw an exception. + return; + } + + v8::Local array = Nan::New(length); + for (uint32_t i = 0; i < length; i++) { + Nan::Set(array, i, Nan::New(i)); + } + + info.GetReturnValue().Set(array); +} + +template +inline void ObjectWrap::set_property(v8::Local property, v8::Local value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) { + if (s_class.index_accessor.getter || s_class.index_accessor.setter) { + try { + // Negative indices are passed into this string property interceptor, so check for them here. + validated_positive_index(node::String(property)); + } + catch (std::out_of_range &e) { + Nan::ThrowError(Exception::value(info.GetIsolate(), e)); + return; + } + catch (std::invalid_argument &) { + // Property is not a number. + } + } + if (auto string_setter = s_class.string_accessor.setter) { + string_setter(property, value, info); + } +} + } // node namespace js { -template -class ObjectWrap : public node::ObjectWrap {}; +template +class ObjectWrap : public node::ObjectWrap {}; template void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { From a5bf9ed8dd02d3c29ff5431b293533e800eb3829 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sun, 17 Apr 2016 22:12:23 -0700 Subject: [PATCH 27/48] Define most of jsc::ObjectWrap separately --- src/jsc/jsc_class.hpp | 437 ++++++++++++++++++++++-------------------- 1 file changed, 229 insertions(+), 208 deletions(-) diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 403f37bf..8281972b 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -38,19 +38,19 @@ using PropertyMap = js::PropertyMap; template class ObjectWrap { -public: using Internal = typename ClassType::Internal; using ParentClassType = typename ClassType::Parent; - operator Internal*() const { - return m_object.get(); + public: + static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { + return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); } - ObjectWrap& operator=(Internal* object) { - if (m_object.get() != object) { - m_object = std::unique_ptr(object); + static JSObjectRef create_constructor(JSContextRef ctx) { + if (JSClassRef constructor_class = get_constructor_class()) { + return JSObjectMake(ctx, constructor_class, nullptr); } - return *this; + return JSObjectMakeConstructor(ctx, get_class(), construct); } static JSClassRef get_class() { @@ -63,221 +63,47 @@ public: return js_class; } - static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) { - return JSObjectMake(ctx, get_class(), new ObjectWrap(internal)); - } - - static JSObjectRef create_constructor(JSContextRef ctx) { - if (JSClassRef constructor_class = get_constructor_class()) { - return JSObjectMake(ctx, constructor_class, nullptr); - } - - return JSObjectMakeConstructor(ctx, get_class(), construct); - } - static bool has_instance(JSContextRef ctx, JSValueRef value) { return JSValueIsObjectOfClass(ctx, value, get_class()); } -private: + operator Internal*() const { + return m_object.get(); + } + + ObjectWrap& operator=(Internal* object) { + if (m_object.get() != object) { + m_object = std::unique_ptr(object); + } + return *this; + } + + private: static ClassType s_class; std::unique_ptr m_object; ObjectWrap(Internal* object = nullptr) : m_object(object) {} - static JSObjectRef construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { - if (!s_class.constructor) { - *exception = jsc::Exception::value(ctx, "Illegal constructor"); - return nullptr; - } + static JSClassRef create_constructor_class(); + static JSClassRef create_class(); - JSObjectRef this_object = ObjectWrap::create_instance(ctx); - try { - s_class.constructor(ctx, this_object, argc, arguments); - } - catch(std::exception &e) { - *exception = jsc::Exception::value(ctx, e); - } - return this_object; - } + static std::vector get_methods(const MethodMap &); + static std::vector get_properties(const PropertyMap &); - static bool has_instance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { - return JSValueIsObjectOfClass(ctx, value, get_class()); - } - - static JSValueRef get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { - if (auto index_getter = s_class.index_accessor.getter) { - try { - uint32_t index = validated_positive_index(jsc::String(property)); - return index_getter(ctx, object, index, exception); - } - catch (std::out_of_range &) { - // Out-of-bounds index getters should just return undefined in JS. - return Value::from_undefined(ctx); - } - catch (std::invalid_argument &) { - // Property is not a number. - } - } - if (auto string_getter = s_class.string_accessor.getter) { - return string_getter(ctx, object, property, exception); - } - return nullptr; - } - - static bool set_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { - auto index_setter = s_class.index_accessor.setter; - - if (index_setter || s_class.index_accessor.getter) { - try { - uint32_t index = validated_positive_index(jsc::String(property)); - - if (index_setter) { - return index_setter(ctx, object, index, value, exception); - } - else { - *exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index)); - return false; - } - } - catch (std::out_of_range &e) { - *exception = Exception::value(ctx, e); - return false; - } - catch (std::invalid_argument &) { - // Property is not a number. - } - } - if (auto string_setter = s_class.string_accessor.setter) { - return string_setter(ctx, object, property, value, exception); - } - return false; - } + static JSObjectRef construct(JSContextRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*); + static void finalize(JSObjectRef); + static void get_property_names(JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef); + static JSValueRef get_property(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*); + static bool set_property(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*); static bool set_readonly_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { *exception = Exception::value(ctx, std::string("Cannot assign to read only property '") + std::string(String(property)) + "'"); return false; } - static void get_property_names(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { - if (s_class.index_accessor.getter) { - try { - uint32_t length = Object::validated_get_length(ctx, object); - char string[32]; - for (uint32_t i = 0; i < length; i++) { - sprintf(string, "%u", i); - JSPropertyNameAccumulatorAddName(accumulator, jsc::String(string)); - } - } - catch (std::exception &) { - // Enumerating properties should never throw an exception into JS. - } - } - if (auto string_enumerator = s_class.string_accessor.enumerator) { - string_enumerator(ctx, object, accumulator); - } - } - - static void finalize(JSObjectRef object) { - // This is called for the most derived class before superclasses. - if (auto wrap = static_cast *>(JSObjectGetPrivate(object))) { - delete wrap; - JSObjectSetPrivate(object, nullptr); - } - } - - static std::vector get_methods(const MethodMap &methods) { - std::vector functions; - functions.reserve(methods.size() + 1); - - JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - size_t index = 0; - - for (auto &pair : methods) { - functions[index++] = {pair.first.c_str(), pair.second, attributes}; - } - - functions[index] = {0}; - return functions; - } - - static std::vector get_properties(const PropertyMap &properties) { - std::vector values; - values.reserve(properties.size() + 1); - - JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; - size_t index = 0; - - for (auto &pair : properties) { - auto &prop = pair.second; - values[index++] = {pair.first.c_str(), prop.getter, prop.setter ?: set_readonly_property, attributes}; - } - - values[index] = {0}; - return values; - } - - static JSClassRef create_class() { - JSClassDefinition definition = kJSClassDefinitionEmpty; - std::vector methods; - std::vector properties; - - definition.parentClass = ObjectWrap::get_class(); - definition.className = s_class.name.c_str(); - definition.finalize = finalize; - - if (!s_class.methods.empty()) { - methods = get_methods(s_class.methods); - definition.staticFunctions = methods.data(); - } - if (!s_class.properties.empty()) { - properties = get_properties(s_class.properties); - definition.staticValues = properties.data(); - } - - if (s_class.index_accessor.getter || s_class.string_accessor.getter) { - definition.getProperty = get_property; - definition.setProperty = set_property; - } - else if (s_class.index_accessor.setter || s_class.string_accessor.setter) { - definition.setProperty = set_property; - } - - if (s_class.index_accessor.getter || s_class.string_accessor.enumerator) { - definition.getPropertyNames = get_property_names; - } - - return JSClassCreate(&definition); - } - - static JSClassRef create_constructor_class() { - // Skip creating a special constructor class if possible. - if (!s_class.constructor && s_class.static_methods.empty() && s_class.static_properties.empty()) { - return nullptr; - } - - JSClassDefinition definition = kJSClassDefinitionEmpty; - std::vector methods; - std::vector properties; - - definition.attributes = kJSClassAttributeNoAutomaticPrototype; - definition.className = s_class.name.c_str(); - definition.hasInstance = has_instance; - - if (s_class.constructor) { - definition.callAsConstructor = construct; - } - if (!s_class.static_methods.empty()) { - methods = get_methods(s_class.static_methods); - definition.staticFunctions = methods.data(); - } - if (!s_class.static_properties.empty()) { - properties = get_properties(s_class.static_properties); - definition.staticValues = properties.data(); - } - - return JSClassCreate(&definition); + static bool has_instance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) { + return JSValueIsObjectOfClass(ctx, value, get_class()); } }; @@ -291,15 +117,210 @@ public: } }; -// The declared static variables must be defined as well. -template T ObjectWrap::s_class; +// The static class variable must be defined as well. +template +ClassType ObjectWrap::s_class; + +template +inline JSClassRef ObjectWrap::create_class() { + JSClassDefinition definition = kJSClassDefinitionEmpty; + std::vector methods; + std::vector properties; + + definition.parentClass = ObjectWrap::get_class(); + definition.className = s_class.name.c_str(); + definition.finalize = finalize; + + if (!s_class.methods.empty()) { + methods = get_methods(s_class.methods); + definition.staticFunctions = methods.data(); + } + if (!s_class.properties.empty()) { + properties = get_properties(s_class.properties); + definition.staticValues = properties.data(); + } + + if (s_class.index_accessor.getter || s_class.string_accessor.getter) { + definition.getProperty = get_property; + definition.setProperty = set_property; + } + else if (s_class.index_accessor.setter || s_class.string_accessor.setter) { + definition.setProperty = set_property; + } + + if (s_class.index_accessor.getter || s_class.string_accessor.enumerator) { + definition.getPropertyNames = get_property_names; + } + + return JSClassCreate(&definition); +} + +template +inline JSClassRef ObjectWrap::create_constructor_class() { + // Skip creating a special constructor class if possible. + if (!s_class.constructor && s_class.static_methods.empty() && s_class.static_properties.empty()) { + return nullptr; + } + + JSClassDefinition definition = kJSClassDefinitionEmpty; + std::vector methods; + std::vector properties; + + definition.attributes = kJSClassAttributeNoAutomaticPrototype; + definition.className = s_class.name.c_str(); + definition.hasInstance = has_instance; + + if (s_class.constructor) { + definition.callAsConstructor = construct; + } + if (!s_class.static_methods.empty()) { + methods = get_methods(s_class.static_methods); + definition.staticFunctions = methods.data(); + } + if (!s_class.static_properties.empty()) { + properties = get_properties(s_class.static_properties); + definition.staticValues = properties.data(); + } + + return JSClassCreate(&definition); +} + +template +inline std::vector ObjectWrap::get_methods(const MethodMap &methods) { + std::vector functions; + functions.reserve(methods.size() + 1); + + JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : methods) { + functions[index++] = {pair.first.c_str(), pair.second, attributes}; + } + + functions[index] = {0}; + return functions; +} + +template +inline std::vector ObjectWrap::get_properties(const PropertyMap &properties) { + std::vector values; + values.reserve(properties.size() + 1); + + JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; + size_t index = 0; + + for (auto &pair : properties) { + auto &prop = pair.second; + values[index++] = {pair.first.c_str(), prop.getter, prop.setter ?: set_readonly_property, attributes}; + } + + values[index] = {0}; + return values; +} + +template +inline JSObjectRef ObjectWrap::construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { + if (!s_class.constructor) { + *exception = jsc::Exception::value(ctx, "Illegal constructor"); + return nullptr; + } + + JSObjectRef this_object = ObjectWrap::create_instance(ctx); + try { + s_class.constructor(ctx, this_object, argc, arguments); + } + catch(std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + } + return this_object; +} + +template +inline void ObjectWrap::finalize(JSObjectRef object) { + // This is called for the most derived class before superclasses. + if (auto wrap = static_cast *>(JSObjectGetPrivate(object))) { + delete wrap; + JSObjectSetPrivate(object, nullptr); + } +} + +template +inline void ObjectWrap::get_property_names(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) { + if (s_class.index_accessor.getter) { + try { + uint32_t length = Object::validated_get_length(ctx, object); + char string[32]; + for (uint32_t i = 0; i < length; i++) { + sprintf(string, "%u", i); + JSPropertyNameAccumulatorAddName(accumulator, jsc::String(string)); + } + } + catch (std::exception &) { + // Enumerating properties should never throw an exception into JS. + } + } + if (auto string_enumerator = s_class.string_accessor.enumerator) { + string_enumerator(ctx, object, accumulator); + } +} + +template +inline JSValueRef ObjectWrap::get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { + if (auto index_getter = s_class.index_accessor.getter) { + try { + uint32_t index = validated_positive_index(jsc::String(property)); + return index_getter(ctx, object, index, exception); + } + catch (std::out_of_range &) { + // Out-of-bounds index getters should just return undefined in JS. + return Value::from_undefined(ctx); + } + catch (std::invalid_argument &) { + // Property is not a number. + } + } + if (auto string_getter = s_class.string_accessor.getter) { + return string_getter(ctx, object, property, exception); + } + return nullptr; +} + +template +inline bool ObjectWrap::set_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) { + auto index_setter = s_class.index_accessor.setter; + + if (index_setter || s_class.index_accessor.getter) { + try { + uint32_t index = validated_positive_index(jsc::String(property)); + + if (index_setter) { + return index_setter(ctx, object, index, value, exception); + } + else { + *exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index)); + return false; + } + } + catch (std::out_of_range &e) { + *exception = Exception::value(ctx, e); + return false; + } + catch (std::invalid_argument &) { + // Property is not a number. + } + } + if (auto string_setter = s_class.string_accessor.setter) { + return string_setter(ctx, object, property, value, exception); + } + return false; +} } // jsc namespace js { -template -class ObjectWrap : public jsc::ObjectWrap {}; +template +class ObjectWrap : public jsc::ObjectWrap {}; template JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { From 60a3cd4399bbe4769b05f6989a66c134668a3cd7 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sun, 17 Apr 2016 22:33:18 -0700 Subject: [PATCH 28/48] Remove unused js_schema.cpp --- src/ios/RealmJS.xcodeproj/project.pbxproj | 4 -- src/js_schema.cpp | 48 ----------------------- 2 files changed, 52 deletions(-) delete mode 100644 src/js_schema.cpp diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 6c1bc8db..8c4fb61a 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; }; F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; }; F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; }; - F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480B1C0428DF00ABDED4 /* js_schema.cpp */; }; F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; }; F63FF2DE1C1565EA00B3B8E0 /* RealmJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */; }; F63FF2E11C1591EC00B3B8E0 /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; }; @@ -125,7 +124,6 @@ 029048071C0428DF00ABDED4 /* js_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_realm.cpp; sourceTree = ""; }; 029048081C0428DF00ABDED4 /* js_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm.hpp; sourceTree = ""; }; 0290480A1C0428DF00ABDED4 /* js_results.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_results.hpp; sourceTree = ""; }; - 0290480B1C0428DF00ABDED4 /* js_schema.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_schema.cpp; sourceTree = ""; }; 0290480C1C0428DF00ABDED4 /* js_schema.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_schema.hpp; sourceTree = ""; }; 0290480F1C0428DF00ABDED4 /* rpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rpc.cpp; sourceTree = ""; }; 029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = ""; }; @@ -283,7 +281,6 @@ 029048071C0428DF00ABDED4 /* js_realm.cpp */, 029048081C0428DF00ABDED4 /* js_realm.hpp */, 0290480A1C0428DF00ABDED4 /* js_results.hpp */, - 0290480B1C0428DF00ABDED4 /* js_schema.cpp */, 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, F620F0521CAF0B600082977B /* js_class.hpp */, F6874A3E1CACA5A900EEEE36 /* js_types.hpp */, @@ -771,7 +768,6 @@ 02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */, F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */, 02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */, - F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */, 02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */, 02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */, 02F59EC41C88F17D007F774C /* schema.cpp in Sources */, diff --git a/src/js_schema.cpp b/src/js_schema.cpp deleted file mode 100644 index 60500f6d..00000000 --- a/src/js_schema.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "js_schema.hpp" -#include "object_store.hpp" - -/* -namespace realm { - struct SchemaWrapper { - Schema *schema; - bool owned; - ~SchemaWrapper() { - if (owned) { - delete schema; - } - } - }; -} - -using namespace realm; - -JSClassRef RJSSchemaClass() { - static JSClassRef s_schemaClass = RJSCreateWrapperClass("Schema"); - return s_schemaClass; -} - -JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { - SchemaWrapper *wrapper = new SchemaWrapper(); - wrapper->schema = &schema; - wrapper->owned = false; - return js::WrapObject(ctx, RJSSchemaClass(), wrapper); -} -*/ From 10f08747e72cae15786b7c59c733860b2275d371 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sun, 17 Apr 2016 22:49:07 -0700 Subject: [PATCH 29/48] Rename remained camelCase to snake_case And fix some minor build warnings along the way. --- src/js_list.hpp | 73 ++++++++++++------------ src/js_object.hpp | 25 +++++---- src/js_realm.cpp | 2 +- src/js_realm.hpp | 134 +++++++++++++++++++++++---------------------- src/js_results.hpp | 33 +++++------ src/js_schema.hpp | 120 ++++++++++++++++++++-------------------- src/rpc.cpp | 5 +- 7 files changed, 199 insertions(+), 193 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index b78883c7..461d9bad 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -33,7 +33,7 @@ namespace realm { namespace js { template -struct List { +class List { using TContext = typename T::Context; using TObject = typename T::Object; using TValue = typename T::Value; @@ -41,20 +41,23 @@ struct List { using Value = Value; using ReturnValue = ReturnValue; + public: static TObject create_instance(TContext, realm::List &); - static void GetLength(TContext, TObject, ReturnValue &); - static void GetIndex(TContext, TObject, uint32_t, ReturnValue &); - static bool SetIndex(TContext, TObject, uint32_t, TValue); + // properties + static void get_length(TContext, TObject, ReturnValue &); + static void get_index(TContext, TObject, uint32_t, ReturnValue &); + static bool set_index(TContext, TObject, uint32_t, TValue); - static void Push(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Pop(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Unshift(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Shift(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Splice(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void StaticResults(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); + // methods + static void push(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void pop(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void unshift(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void shift(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void splice(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void snapshot(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); }; template @@ -64,21 +67,21 @@ struct ListClass : ClassDefinition> { std::string const name = "List"; MethodMap const methods = { - {"push", wrap}, - {"pop", wrap}, - {"unshift", wrap}, - {"shift", wrap}, - {"splice", wrap}, - {"snapshot", wrap}, - {"filtered", wrap}, - {"sorted", wrap}, + {"push", wrap}, + {"pop", wrap}, + {"unshift", wrap}, + {"shift", wrap}, + {"splice", wrap}, + {"snapshot", wrap}, + {"filtered", wrap}, + {"sorted", wrap}, }; PropertyMap const properties = { - {"length", {wrap}}, + {"length", {wrap, nullptr}}, }; - IndexPropertyType const index_accessor = {wrap, wrap}; + IndexPropertyType const index_accessor = {wrap, wrap}; }; template @@ -87,13 +90,13 @@ typename T::Object List::create_instance(TContext ctx, realm::List &list) { } template -void List::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { +void List::get_length(TContext ctx, TObject object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } template -void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { +void List::get_index(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { auto list = get_internal>(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); @@ -101,14 +104,14 @@ void List::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue } template -bool List::SetIndex(TContext ctx, TObject object, uint32_t index, TValue value) { +bool List::set_index(TContext ctx, TObject object, uint32_t index, TValue value) { auto list = get_internal>(object); list->set(ctx, value, index); return true; } template -void List::Push(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::push(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -120,7 +123,7 @@ void List::Push(TContext ctx, TObject this_object, size_t argc, const TValue } template -void List::Pop(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::pop(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -139,7 +142,7 @@ void List::Pop(TContext ctx, TObject this_object, size_t argc, const TValue a } template -void List::Unshift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::unshift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -151,7 +154,7 @@ void List::Unshift(TContext ctx, TObject this_object, size_t argc, const TVal } template -void List::Shift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::shift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -168,7 +171,7 @@ void List::Shift(TContext ctx, TObject this_object, size_t argc, const TValue } template -void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::splice(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -177,8 +180,8 @@ void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValu if (index < 0) { index = std::max(size + index, 0); } - - long remove; + + size_t remove; if (argc < 2) { remove = size - index; } @@ -204,7 +207,7 @@ void List::Splice(TContext ctx, TObject this_object, size_t argc, const TValu } template -void List::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::snapshot(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -212,7 +215,7 @@ void List::StaticResults(TContext ctx, TObject this_object, size_t argc, cons } template -void List::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -220,7 +223,7 @@ void List::Filtered(TContext ctx, TObject this_object, size_t argc, const TVa } template -void List::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto list = get_internal>(this_object); diff --git a/src/js_object.hpp b/src/js_object.hpp index 9872706b..b3629747 100644 --- a/src/js_object.hpp +++ b/src/js_object.hpp @@ -29,7 +29,7 @@ namespace realm { namespace js { template -struct RealmObject { +class RealmObject { using TContext = typename T::Context; using TFunction = typename T::Function; using TObject = typename T::Object; @@ -40,11 +40,12 @@ struct RealmObject { using Function = Function; using ReturnValue = ReturnValue; + public: static TObject create_instance(TContext, realm::Object &); - static void GetProperty(TContext, TObject, const String &, ReturnValue &); - static bool SetProperty(TContext, TObject, const String &, TValue); - static std::vector GetPropertyNames(TContext, TObject); + static void get_property(TContext, TObject, const String &, ReturnValue &); + static bool set_property(TContext, TObject, const String &, TValue); + static std::vector get_property_names(TContext, TObject); }; template @@ -54,15 +55,15 @@ struct RealmObjectClass : ClassDefinition { const std::string name = "RealmObject"; const StringPropertyType string_accessor = { - wrap, - wrap, - wrap, + wrap, + wrap, + wrap, }; }; template typename T::Object RealmObject::create_instance(TContext ctx, realm::Object &realm_object) { - static String s_prototype = "prototype"; + static String prototype_string = "prototype"; auto delegate = get_delegate(realm_object.realm().get()); auto name = realm_object.get_object_schema().name; @@ -73,7 +74,7 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & } TFunction constructor = delegate->m_constructors.at(name); - TObject prototype = Object::validated_get_object(ctx, constructor, s_prototype); + TObject prototype = Object::validated_get_object(ctx, constructor, prototype_string); Object::set_prototype(ctx, object, prototype); TValue result = Function::call(ctx, constructor, object, 0, NULL); @@ -85,7 +86,7 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & } template -void RealmObject::GetProperty(TContext ctx, TObject object, const String &property, ReturnValue &return_value) { +void RealmObject::get_property(TContext ctx, TObject object, const String &property, ReturnValue &return_value) { try { auto realm_object = get_internal>(object); auto result = realm_object->template get_property_value(ctx, property); @@ -96,14 +97,14 @@ void RealmObject::GetProperty(TContext ctx, TObject object, const String &pro } template -bool RealmObject::SetProperty(TContext ctx, TObject object, const String &property, TValue value) { +bool RealmObject::set_property(TContext ctx, TObject object, const String &property, TValue value) { auto realm_object = get_internal>(object); realm_object->set_property_value(ctx, property, value, true); return true; } template -std::vector> RealmObject::GetPropertyNames(TContext ctx, TObject object) { +std::vector> RealmObject::get_property_names(TContext ctx, TObject object) { auto realm_object = get_internal>(object); auto &properties = realm_object->get_object_schema().properties; diff --git a/src/js_realm.cpp b/src/js_realm.cpp index 12932f93..83152c95 100644 --- a/src/js_realm.cpp +++ b/src/js_realm.cpp @@ -35,7 +35,7 @@ void set_default_path(std::string path) { s_default_path = path; } -void clear_test_state() { +void delete_all_realms() { realm::_impl::RealmCoordinator::clear_all_caches(); realm::remove_realm_files_from_directory(realm::default_realm_file_directory()); } diff --git a/src/js_realm.hpp b/src/js_realm.hpp index b7609391..ac2c8b78 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -113,7 +113,7 @@ class RealmDelegate : public BindingContext { std::string default_path(); void set_default_path(std::string path); -void clear_test_state(); +void delete_all_realms(); template class Realm { @@ -128,44 +128,33 @@ class Realm { using NativeAccessor = realm::NativeAccessor; public: - // member methods - static void Objects(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Create(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Delete(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void DeleteAll(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Write(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void AddListener(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void RemoveListener(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void RemoveAllListeners(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Close(TContext, TObject, size_t, const TValue[], ReturnValue &); + static TFunction create_constructor(TContext); + + // methods + static void objects(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void create(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void delete_one(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void delete_all(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void write(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void add_listener(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void remove_listener(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void remove_all_listeners(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void close(TContext, TObject, size_t, const TValue[], ReturnValue &); // properties - static void GetPath(TContext, TObject, ReturnValue &); - static void GetSchemaVersion(TContext, TObject, ReturnValue &); + static void get_path(TContext, TObject, ReturnValue &); + static void get_schema_version(TContext, TObject, ReturnValue &); - // constructor methods - static void Constructor(TContext, TObject, size_t, const TValue[]); - static void SchemaVersion(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void ClearTestState(TContext, TObject, size_t, const TValue[], ReturnValue &); + // static methods + static void constructor(TContext, TObject, size_t, const TValue[]); + static void schema_version(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void clear_test_state(TContext, TObject, size_t, const TValue[], ReturnValue &); // static properties - static void GetDefaultPath(TContext, TObject, ReturnValue &); - static void SetDefaultPath(TContext, TObject, TValue value); - - static TFunction create_constructor(TContext ctx) { - TFunction realm_constructor = ObjectWrap>::create_constructor(ctx); - TFunction collection_constructor = ObjectWrap>::create_constructor(ctx); - TFunction list_constructor = ObjectWrap>::create_constructor(ctx); - TFunction results_constructor = ObjectWrap>::create_constructor(ctx); - - PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); - Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); - Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes); - Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes); - - return realm_constructor; - } + static void get_default_path(TContext, TObject, ReturnValue &); + static void set_default_path(TContext, TObject, TValue value); + private: static std::string validated_notification_name(TContext ctx, const TValue &value) { std::string name = Value::validated_to_string(ctx, value, "notification name"); if (name != "change") { @@ -204,37 +193,52 @@ struct RealmClass : ClassDefinition { std::string const name = "Realm"; - ConstructorType* const constructor = Realm::Constructor; + ConstructorType* const constructor = Realm::constructor; MethodMap const static_methods = { - {"schemaVersion", wrap}, - {"clearTestState", wrap}, + {"schemaVersion", wrap}, + {"clearTestState", wrap}, }; PropertyMap const static_properties = { - {"defaultPath", {wrap, wrap}}, + {"defaultPath", {wrap, wrap}}, }; MethodMap const methods = { - {"objects", wrap}, - {"create", wrap}, - {"delete", wrap}, - {"deleteAll", wrap}, - {"write", wrap}, - {"addListener", wrap}, - {"removeListener", wrap}, - {"removeAllListeners", wrap}, - {"close", wrap}, + {"objects", wrap}, + {"create", wrap}, + {"delete", wrap}, + {"deleteAll", wrap}, + {"write", wrap}, + {"addListener", wrap}, + {"removeListener", wrap}, + {"removeAllListeners", wrap}, + {"close", wrap}, }; PropertyMap const properties = { - {"path", {wrap}}, - {"schemaVersion", {wrap}}, + {"path", {wrap, nullptr}}, + {"schemaVersion", {wrap, nullptr}}, }; }; template -void Realm::Constructor(TContext ctx, TObject this_object, size_t argc, const TValue arguments[]) { +inline typename T::Function Realm::create_constructor(TContext ctx) { + TFunction realm_constructor = ObjectWrap>::create_constructor(ctx); + TFunction collection_constructor = ObjectWrap>::create_constructor(ctx); + TFunction list_constructor = ObjectWrap>::create_constructor(ctx); + TFunction results_constructor = ObjectWrap>::create_constructor(ctx); + + PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); + Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); + Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes); + Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes); + + return realm_constructor; +} + +template +void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const TValue arguments[]) { static const String path_string = "path"; static const String schema_string = "schema"; static const String schema_version_string = "schemaVersion"; @@ -305,7 +309,7 @@ void Realm::Constructor(TContext ctx, TObject this_object, size_t argc, const } template -void Realm::SchemaVersion(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::schema_version(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); realm::Realm::Config config; @@ -326,35 +330,35 @@ void Realm::SchemaVersion(TContext ctx, TObject this_object, size_t argc, con } template -void Realm::ClearTestState(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::clear_test_state(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); - clear_test_state(); + delete_all_realms(); } template -void Realm::GetDefaultPath(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_default_path(TContext ctx, TObject object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); } template -void Realm::SetDefaultPath(TContext ctx, TObject object, TValue value) { +void Realm::set_default_path(TContext ctx, TObject object, TValue value) { js::set_default_path(Value::validated_to_string(ctx, value, "defaultPath")); } template -void Realm::GetPath(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_path(TContext ctx, TObject object, ReturnValue &return_value) { std::string path = get_internal>(object)->get()->config().path; return_value.set(path); } template -void Realm::GetSchemaVersion(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_schema_version(TContext ctx, TObject object, ReturnValue &return_value) { double version = get_internal>(object)->get()->config().schema_version; return_value.set(version); } template -void Realm::Objects(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::objects(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); @@ -364,7 +368,7 @@ void Realm::Objects(TContext ctx, TObject this_object, size_t argc, const TVa } template -void Realm::Create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2, 3); SharedRealm sharedRealm = *get_internal>(this_object); @@ -391,7 +395,7 @@ void Realm::Create(TContext ctx, TObject this_object, size_t argc, const TVal } template -void Realm::Delete(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::delete_one(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); @@ -434,7 +438,7 @@ void Realm::Delete(TContext ctx, TObject this_object, size_t argc, const TVal } template -void Realm::DeleteAll(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::delete_all(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal>(this_object); @@ -449,7 +453,7 @@ void Realm::DeleteAll(TContext ctx, TObject this_object, size_t argc, const T } template -void Realm::Write(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::write(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); @@ -469,7 +473,7 @@ void Realm::Write(TContext ctx, TObject this_object, size_t argc, const TValu } template -void Realm::AddListener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::add_listener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -480,7 +484,7 @@ void Realm::AddListener(TContext ctx, TObject this_object, size_t argc, const } template -void Realm::RemoveListener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::remove_listener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -491,7 +495,7 @@ void Realm::RemoveListener(TContext ctx, TObject this_object, size_t argc, co } template -void Realm::RemoveAllListeners(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::remove_all_listeners(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0, 1); if (argc) { validated_notification_name(ctx, arguments[0]); @@ -502,7 +506,7 @@ void Realm::RemoveAllListeners(TContext ctx, TObject this_object, size_t argc } template -void Realm::Close(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::close(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal>(this_object); diff --git a/src/js_results.hpp b/src/js_results.hpp index 641293f9..a012f23d 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -30,7 +30,7 @@ namespace realm { namespace js { template -struct Results { +class Results { using TContext = typename T::Context; using TObject = typename T::Object; using TValue = typename T::Value; @@ -38,6 +38,7 @@ struct Results { using Value = Value; using ReturnValue = ReturnValue; + public: static TObject create_instance(TContext, const realm::Results &, bool live = true); static TObject create_instance(TContext, const realm::List &, bool live = true); static TObject create_instance(TContext, SharedRealm, const std::string &type, bool live = true); @@ -49,12 +50,12 @@ struct Results { template static TObject create_sorted(TContext, const U &, size_t, const TValue[]); - static void GetLength(TContext, TObject, ReturnValue &); - static void GetIndex(TContext, TObject, uint32_t, ReturnValue &); + static void get_length(TContext, TObject, ReturnValue &); + static void get_index(TContext, TObject, uint32_t, ReturnValue &); - static void StaticResults(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void Sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void snapshot(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); }; template @@ -64,16 +65,16 @@ struct ResultsClass : ClassDefinition> { std::string const name = "Results"; MethodMap const methods = { - {"snapshot", wrap}, - {"filtered", wrap}, - {"sorted", wrap}, + {"snapshot", wrap}, + {"filtered", wrap}, + {"sorted", wrap}, }; PropertyMap const properties = { - {"length", {wrap}}, + {"length", {wrap, nullptr}}, }; - IndexPropertyType const index_accessor = {wrap}; + IndexPropertyType const index_accessor = {wrap, nullptr}; }; template @@ -194,13 +195,13 @@ typename T::Object Results::create_sorted(TContext ctx, const U &collection, } template -void Results::GetLength(TContext ctx, TObject object, ReturnValue &return_value) { +void Results::get_length(TContext ctx, TObject object, ReturnValue &return_value) { auto results = get_internal>(object); return_value.set((uint32_t)results->size()); } template -void Results::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { +void Results::get_index(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { auto results = get_internal>(object); auto row = results->get(index); @@ -215,7 +216,7 @@ void Results::GetIndex(TContext ctx, TObject object, uint32_t index, ReturnVa } template -void Results::StaticResults(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::snapshot(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto results = get_internal>(this_object); @@ -223,7 +224,7 @@ void Results::StaticResults(TContext ctx, TObject this_object, size_t argc, c } template -void Results::Filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto results = get_internal>(this_object); @@ -231,7 +232,7 @@ void Results::Filtered(TContext ctx, TObject this_object, size_t argc, const } template -void Results::Sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto results = get_internal>(this_object); diff --git a/src/js_schema.hpp b/src/js_schema.hpp index 9ad69b97..75fc310f 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -65,26 +65,26 @@ typename T::Object Schema::dict_for_property_array(TContext ctx, const Object } template -Property Schema::parse_property(TContext ctx, TValue attributes, std::string propertyName, ObjectDefaults &objectDefaults) { - static const String defaultString = "default"; - static const String indexedString = "indexed"; - static const String typeString = "type"; - static const String objectTypeString = "objectType"; - static const String optionalString = "optional"; +Property Schema::parse_property(TContext ctx, TValue attributes, std::string property_name, ObjectDefaults &object_defaults) { + static const String default_string = "default"; + static const String indexed_string = "indexed"; + static const String type_string = "type"; + static const String object_type_string = "objectType"; + static const String optional_string = "optional"; Property prop; - prop.name = propertyName; + prop.name = property_name; - TObject propertyObject = {}; + TObject property_object = {}; std::string type; if (Value::is_object(ctx, attributes)) { - propertyObject = Value::validated_to_object(ctx, attributes); - type = Object::validated_get_string(ctx, propertyObject, typeString); + property_object = Value::validated_to_object(ctx, attributes); + type = Object::validated_get_string(ctx, property_object, type_string); - TValue optionalValue = Object::get_property(ctx, propertyObject, optionalString); - if (!Value::is_undefined(ctx, optionalValue)) { - prop.is_nullable = Value::validated_to_boolean(ctx, optionalValue, "optional"); + TValue optional_value = Object::get_property(ctx, property_object, optional_string); + if (!Value::is_undefined(ctx, optional_value)) { + prop.is_nullable = Value::validated_to_boolean(ctx, optional_value, "optional"); } } else { @@ -113,11 +113,11 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string prop.type = PropertyTypeData; } else if (type == "list") { - if (!Value::is_valid(propertyObject)) { + if (!Value::is_valid(property_object)) { throw std::runtime_error("List property must specify 'objectType'"); } prop.type = PropertyTypeArray; - prop.object_type = Object::validated_get_string(ctx, propertyObject, objectTypeString); + prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string); } else { prop.type = PropertyTypeObject; @@ -125,25 +125,25 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string // The type could either be 'object' or the name of another object type in the same schema. if (type == "object") { - if (!Value::is_valid(propertyObject)) { + if (!Value::is_valid(property_object)) { throw std::runtime_error("Object property must specify 'objectType'"); } - prop.object_type = Object::validated_get_string(ctx, propertyObject, objectTypeString); + prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string); } else { prop.object_type = type; } } - if (Value::is_valid(propertyObject)) { - TValue defaultValue = Object::get_property(ctx, propertyObject, defaultString); - if (!Value::is_undefined(ctx, defaultValue)) { - objectDefaults.emplace(prop.name, Protected(ctx, defaultValue)); + if (Value::is_valid(property_object)) { + TValue default_value = Object::get_property(ctx, property_object, default_string); + if (!Value::is_undefined(ctx, default_value)) { + object_defaults.emplace(prop.name, Protected(ctx, default_value)); } - TValue indexedValue = Object::get_property(ctx, propertyObject, indexedString); - if (!Value::is_undefined(ctx, indexedValue)) { - prop.is_indexed = Value::validated_to_boolean(ctx, indexedValue); + TValue indexed_value = Object::get_property(ctx, property_object, indexed_string); + if (!Value::is_undefined(ctx, indexed_value)) { + prop.is_indexed = Value::validated_to_boolean(ctx, indexed_value); } } @@ -151,68 +151,68 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string } template -ObjectSchema Schema::parse_object_schema(TContext ctx, TObject objectSchemaObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { - static const String nameString = "name"; - static const String primaryString = "primaryKey"; - static const String propertiesString = "properties"; - static const String schemaString = "schema"; +ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { + static const String name_string = "name"; + static const String primary_string = "primaryKey"; + static const String properties_string = "properties"; + static const String schema_string = "schema"; - TFunction objectConstructor = {}; - if (Value::is_constructor(ctx, objectSchemaObject)) { - objectConstructor = Value::to_constructor(ctx, objectSchemaObject); - objectSchemaObject = Object::validated_get_object(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property."); + TFunction object_constructor = {}; + if (Value::is_constructor(ctx, object_schema_object)) { + object_constructor = Value::to_constructor(ctx, object_schema_object); + object_schema_object = Object::validated_get_object(ctx, object_constructor, schema_string, "Realm object constructor must have a 'schema' property."); } - ObjectDefaults objectDefaults; - ObjectSchema objectSchema; - objectSchema.name = Object::validated_get_string(ctx, objectSchemaObject, nameString); + ObjectDefaults object_defaults; + ObjectSchema object_schema; + object_schema.name = Object::validated_get_string(ctx, object_schema_object, name_string); - TObject propertiesObject = Object::validated_get_object(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object."); - if (Value::is_array(ctx, propertiesObject)) { - uint32_t length = Object::validated_get_length(ctx, propertiesObject); + TObject properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema must have a 'properties' object."); + if (Value::is_array(ctx, properties_object)) { + uint32_t length = Object::validated_get_length(ctx, properties_object); for (uint32_t i = 0; i < length; i++) { - TObject propertyObject = Object::validated_get_object(ctx, propertiesObject, i); - std::string propertyName = Object::validated_get_string(ctx, propertyObject, nameString); - objectSchema.properties.emplace_back(parse_property(ctx, propertyObject, propertyName, objectDefaults)); + TObject property_object = Object::validated_get_object(ctx, properties_object, i); + std::string property_name = Object::validated_get_string(ctx, property_object, name_string); + object_schema.properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults)); } } else { - auto propertyNames = Object::get_property_names(ctx, propertiesObject); - for (auto &propertyName : propertyNames) { - TValue propertyValue = Object::get_property(ctx, propertiesObject, propertyName); - objectSchema.properties.emplace_back(parse_property(ctx, propertyValue, propertyName, objectDefaults)); + auto property_names = Object::get_property_names(ctx, properties_object); + for (auto &property_name : property_names) { + TValue property_value = Object::get_property(ctx, properties_object, property_name); + object_schema.properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults)); } } - TValue primaryValue = Object::get_property(ctx, objectSchemaObject, primaryString); - if (!Value::is_undefined(ctx, primaryValue)) { - objectSchema.primary_key = Value::validated_to_string(ctx, primaryValue); - Property *property = objectSchema.primary_key_property(); + TValue primary_value = Object::get_property(ctx, object_schema_object, primary_string); + if (!Value::is_undefined(ctx, primary_value)) { + object_schema.primary_key = Value::validated_to_string(ctx, primary_value); + Property *property = object_schema.primary_key_property(); if (!property) { - throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'"); + throw std::runtime_error("Missing primary key property '" + object_schema.primary_key + "'"); } property->is_primary = true; } // Store prototype so that objects of this type will have their prototype set to this prototype object. - if (Value::is_valid(objectConstructor)) { - constructors.emplace(objectSchema.name, Protected(ctx, objectConstructor)); + if (Value::is_valid(object_constructor)) { + constructors.emplace(object_schema.name, Protected(ctx, object_constructor)); } - defaults.emplace(objectSchema.name, std::move(objectDefaults)); + defaults.emplace(object_schema.name, std::move(object_defaults)); - return objectSchema; + return object_schema; } template -realm::Schema Schema::parse_schema(TContext ctx, TObject jsonObject, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { +realm::Schema Schema::parse_schema(TContext ctx, TObject schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { std::vector schema; - uint32_t length = Object::validated_get_length(ctx, jsonObject); + uint32_t length = Object::validated_get_length(ctx, schema_object); for (uint32_t i = 0; i < length; i++) { - TObject jsonObjectSchema = Object::validated_get_object(ctx, jsonObject, i); - ObjectSchema objectSchema = parse_object_schema(ctx, jsonObjectSchema, defaults, constructors); - schema.emplace_back(std::move(objectSchema)); + TObject object_schema_object = Object::validated_get_object(ctx, schema_object, i); + ObjectSchema object_schema = parse_object_schema(ctx, object_schema_object, defaults, constructors); + schema.emplace_back(std::move(object_schema)); } return realm::Schema(schema); diff --git a/src/rpc.cpp b/src/rpc.cpp index bd50187f..e30a8efd 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -25,9 +25,6 @@ #include "jsc_init.hpp" #include "jsc_types.hpp" -#include "js_object.hpp" -#include "js_results.hpp" -#include "js_realm.hpp" #include "base64.hpp" #include "object_accessor.hpp" @@ -166,7 +163,7 @@ RPCServer::RPCServer() { m_objects.erase(object.first); } JSGarbageCollect(m_context); - js::clear_test_state(); + js::delete_all_realms(); return json::object(); }; } From d57483c675260471e7046b243a10421fa1d024ed Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 01:14:48 -0700 Subject: [PATCH 30/48] Rename js_object.hpp -> js_realm_object.hpp --- src/ios/RealmJS.xcodeproj/project.pbxproj | 8 ++++---- src/js_list.hpp | 2 +- src/js_object_accessor.hpp | 2 +- src/js_realm.hpp | 2 +- src/{js_object.hpp => js_realm_object.hpp} | 0 src/js_results.hpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename src/{js_object.hpp => js_realm_object.hpp} (100%) diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index 8c4fb61a..c149313f 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; }; 029048121C0428DF00ABDED4 /* jsc_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* jsc_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; 029048141C0428DF00ABDED4 /* js_list.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048041C0428DF00ABDED4 /* js_list.hpp */; }; - 029048161C0428DF00ABDED4 /* js_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048061C0428DF00ABDED4 /* js_object.hpp */; }; + 029048161C0428DF00ABDED4 /* js_realm_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048061C0428DF00ABDED4 /* js_realm_object.hpp */; }; 029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048081C0428DF00ABDED4 /* js_realm.hpp */; }; 0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480A1C0428DF00ABDED4 /* js_results.hpp */; }; 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480C1C0428DF00ABDED4 /* js_schema.hpp */; }; @@ -120,7 +120,7 @@ 029048011C0428DF00ABDED4 /* jsc_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_init.cpp; sourceTree = ""; }; 029048021C0428DF00ABDED4 /* jsc_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsc_init.h; sourceTree = ""; }; 029048041C0428DF00ABDED4 /* js_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_list.hpp; sourceTree = ""; }; - 029048061C0428DF00ABDED4 /* js_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_object.hpp; sourceTree = ""; }; + 029048061C0428DF00ABDED4 /* js_realm_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm_object.hpp; sourceTree = ""; }; 029048071C0428DF00ABDED4 /* js_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_realm.cpp; sourceTree = ""; }; 029048081C0428DF00ABDED4 /* js_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm.hpp; sourceTree = ""; }; 0290480A1C0428DF00ABDED4 /* js_results.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_results.hpp; sourceTree = ""; }; @@ -277,7 +277,7 @@ F60102F71CBDA6D400EC01BA /* js_collection.hpp */, 029048041C0428DF00ABDED4 /* js_list.hpp */, F620F0591CB7B4C80082977B /* js_object_accessor.hpp */, - 029048061C0428DF00ABDED4 /* js_object.hpp */, + 029048061C0428DF00ABDED4 /* js_realm_object.hpp */, 029048071C0428DF00ABDED4 /* js_realm.cpp */, 029048081C0428DF00ABDED4 /* js_realm.hpp */, 0290480A1C0428DF00ABDED4 /* js_results.hpp */, @@ -515,7 +515,7 @@ 029048141C0428DF00ABDED4 /* js_list.hpp in Headers */, F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */, 0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */, - 029048161C0428DF00ABDED4 /* js_object.hpp in Headers */, + 029048161C0428DF00ABDED4 /* js_realm_object.hpp in Headers */, 029048371C042A3C00ABDED4 /* platform.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/src/js_list.hpp b/src/js_list.hpp index 461d9bad..04959f04 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -19,7 +19,7 @@ #pragma once #include "js_collection.hpp" -#include "js_object.hpp" +#include "js_realm_object.hpp" #include "js_results.hpp" #include "js_types.hpp" #include "js_util.hpp" diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index 8f3ed85f..89c332d4 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -19,7 +19,7 @@ #pragma once #include "js_list.hpp" -#include "js_object.hpp" +#include "js_realm_object.hpp" #include "js_schema.hpp" namespace realm { diff --git a/src/js_realm.hpp b/src/js_realm.hpp index ac2c8b78..a893cb51 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -24,7 +24,7 @@ #include "js_class.hpp" #include "js_types.hpp" #include "js_util.hpp" -#include "js_object.hpp" +#include "js_realm_object.hpp" #include "js_list.hpp" #include "js_results.hpp" #include "js_schema.hpp" diff --git a/src/js_object.hpp b/src/js_realm_object.hpp similarity index 100% rename from src/js_object.hpp rename to src/js_realm_object.hpp diff --git a/src/js_results.hpp b/src/js_results.hpp index a012f23d..fb6a516a 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -19,7 +19,7 @@ #pragma once #include "js_collection.hpp" -#include "js_object.hpp" +#include "js_realm_object.hpp" #include "results.hpp" #include "list.hpp" From 5c56a994036633636c5daba09fbb8f54027fffb8 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 01:15:54 -0700 Subject: [PATCH 31/48] Split up JS abstractions into type-specific files --- src/ios/RealmJS.xcodeproj/project.pbxproj | 48 ++- src/jsc/jsc_class.hpp | 1 + src/jsc/jsc_context.hpp | 32 ++ src/jsc/jsc_exception.hpp | 33 ++ src/jsc/jsc_function.hpp | 47 +++ src/jsc/jsc_init.hpp | 9 + src/jsc/jsc_object.hpp | 147 ++++++++ src/jsc/jsc_protected.hpp | 78 ++++ src/jsc/jsc_return_value.hpp | 64 ++++ src/jsc/jsc_string.hpp | 59 +++ src/jsc/jsc_types.hpp | 440 +--------------------- src/jsc/jsc_value.hpp | 194 ++++++++++ src/node/node_class.hpp | 1 + src/node/node_context.hpp | 32 ++ src/node/node_exception.hpp | 32 ++ src/node/node_function.hpp | 49 +++ src/node/node_init.hpp | 9 + src/node/node_object.hpp | 159 ++++++++ src/node/node_protected.hpp | 84 +++++ src/node/node_return_value.hpp | 65 ++++ src/node/node_string.hpp | 45 +++ src/node/node_types.hpp | 421 +-------------------- src/node/node_value.hpp | 169 +++++++++ 23 files changed, 1355 insertions(+), 863 deletions(-) create mode 100644 src/jsc/jsc_context.hpp create mode 100644 src/jsc/jsc_exception.hpp create mode 100644 src/jsc/jsc_function.hpp create mode 100644 src/jsc/jsc_object.hpp create mode 100644 src/jsc/jsc_protected.hpp create mode 100644 src/jsc/jsc_return_value.hpp create mode 100644 src/jsc/jsc_string.hpp create mode 100644 src/jsc/jsc_value.hpp create mode 100644 src/node/node_context.hpp create mode 100644 src/node/node_exception.hpp create mode 100644 src/node/node_function.hpp create mode 100644 src/node/node_object.hpp create mode 100644 src/node/node_protected.hpp create mode 100644 src/node/node_return_value.hpp create mode 100644 src/node/node_string.hpp create mode 100644 src/node/node_value.hpp diff --git a/src/ios/RealmJS.xcodeproj/project.pbxproj b/src/ios/RealmJS.xcodeproj/project.pbxproj index c149313f..2e65c234 100644 --- a/src/ios/RealmJS.xcodeproj/project.pbxproj +++ b/src/ios/RealmJS.xcodeproj/project.pbxproj @@ -173,6 +173,22 @@ F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = ""; }; F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = ""; }; F60102F71CBDA6D400EC01BA /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = ""; }; + F60103071CC4B3DF00EC01BA /* node_protected.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_protected.hpp; sourceTree = ""; }; + F60103081CC4B4F900EC01BA /* jsc_protected.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_protected.hpp; sourceTree = ""; }; + F60103091CC4B5E800EC01BA /* jsc_context.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_context.hpp; sourceTree = ""; }; + F601030A1CC4B64E00EC01BA /* node_context.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_context.hpp; sourceTree = ""; }; + F601030B1CC4B6C900EC01BA /* jsc_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_value.hpp; sourceTree = ""; }; + F601030C1CC4B72B00EC01BA /* node_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_value.hpp; sourceTree = ""; }; + F601030D1CC4B76F00EC01BA /* jsc_object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_object.hpp; sourceTree = ""; }; + F601030E1CC4B7C900EC01BA /* node_object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_object.hpp; sourceTree = ""; }; + F601030F1CC4B80800EC01BA /* jsc_function.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_function.hpp; sourceTree = ""; }; + F60103101CC4B86000EC01BA /* node_function.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_function.hpp; sourceTree = ""; }; + F60103111CC4BA6500EC01BA /* jsc_exception.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_exception.hpp; sourceTree = ""; }; + F60103121CC4CBF000EC01BA /* node_exception.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_exception.hpp; sourceTree = ""; }; + F60103131CC4CC4500EC01BA /* jsc_string.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_string.hpp; sourceTree = ""; }; + F60103141CC4CC8C00EC01BA /* jsc_return_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_return_value.hpp; sourceTree = ""; }; + F60103151CC4CCFD00EC01BA /* node_return_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_return_value.hpp; sourceTree = ""; }; + F60103161CC4CD2F00EC01BA /* node_string.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_string.hpp; sourceTree = ""; }; F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; F620F0521CAF0B600082977B /* js_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_class.hpp; sourceTree = ""; }; F620F0531CAF2EF70082977B /* jsc_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_class.hpp; sourceTree = ""; }; @@ -271,20 +287,17 @@ children = ( F6BCCFDF1C83809A00FE31AE /* lib */, F62A35131C18E6E2004A917D /* iOS */, + F60103051CC4ADE500EC01BA /* JS */, F6874A441CAD2ACD00EEEE36 /* JSC */, F62BF9001CAC72C40022BCDC /* Node */, F62A35141C18E783004A917D /* Object Store */, F60102F71CBDA6D400EC01BA /* js_collection.hpp */, 029048041C0428DF00ABDED4 /* js_list.hpp */, - F620F0591CB7B4C80082977B /* js_object_accessor.hpp */, 029048061C0428DF00ABDED4 /* js_realm_object.hpp */, 029048071C0428DF00ABDED4 /* js_realm.cpp */, 029048081C0428DF00ABDED4 /* js_realm.hpp */, 0290480A1C0428DF00ABDED4 /* js_results.hpp */, 0290480C1C0428DF00ABDED4 /* js_schema.hpp */, - F620F0521CAF0B600082977B /* js_class.hpp */, - F6874A3E1CACA5A900EEEE36 /* js_types.hpp */, - F6267BC91CADC30000AC36B1 /* js_util.hpp */, 029048351C042A3C00ABDED4 /* platform.hpp */, 0290480F1C0428DF00ABDED4 /* rpc.cpp */, 029048101C0428DF00ABDED4 /* rpc.hpp */, @@ -346,6 +359,17 @@ name = Frameworks; sourceTree = ""; }; + F60103051CC4ADE500EC01BA /* JS */ = { + isa = PBXGroup; + children = ( + F620F0521CAF0B600082977B /* js_class.hpp */, + F6874A3E1CACA5A900EEEE36 /* js_types.hpp */, + F6267BC91CADC30000AC36B1 /* js_util.hpp */, + F620F0591CB7B4C80082977B /* js_object_accessor.hpp */, + ); + name = JS; + sourceTree = ""; + }; F62A35131C18E6E2004A917D /* iOS */ = { isa = PBXGroup; children = ( @@ -408,6 +432,14 @@ F620F0571CB766DA0082977B /* node_init.cpp */, F620F0551CB655A50082977B /* node_class.hpp */, F6874A351CAC792D00EEEE36 /* node_types.hpp */, + F60103161CC4CD2F00EC01BA /* node_string.hpp */, + F601030A1CC4B64E00EC01BA /* node_context.hpp */, + F601030C1CC4B72B00EC01BA /* node_value.hpp */, + F601030E1CC4B7C900EC01BA /* node_object.hpp */, + F60103101CC4B86000EC01BA /* node_function.hpp */, + F60103121CC4CBF000EC01BA /* node_exception.hpp */, + F60103071CC4B3DF00EC01BA /* node_protected.hpp */, + F60103151CC4CCFD00EC01BA /* node_return_value.hpp */, F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */, ); name = Node; @@ -482,6 +514,14 @@ 029048011C0428DF00ABDED4 /* jsc_init.cpp */, F620F0531CAF2EF70082977B /* jsc_class.hpp */, 025678951CAB392000FB8501 /* jsc_types.hpp */, + F60103131CC4CC4500EC01BA /* jsc_string.hpp */, + F60103091CC4B5E800EC01BA /* jsc_context.hpp */, + F601030B1CC4B6C900EC01BA /* jsc_value.hpp */, + F601030D1CC4B76F00EC01BA /* jsc_object.hpp */, + F601030F1CC4B80800EC01BA /* jsc_function.hpp */, + F60103111CC4BA6500EC01BA /* jsc_exception.hpp */, + F60103081CC4B4F900EC01BA /* jsc_protected.hpp */, + F60103141CC4CC8C00EC01BA /* jsc_return_value.hpp */, F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */, ); name = JSC; diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 8281972b..e5612cda 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -19,6 +19,7 @@ #pragma once #include "jsc_types.hpp" + #include "js_class.hpp" #include "js_util.hpp" diff --git a/src/jsc/jsc_context.hpp b/src/jsc/jsc_context.hpp new file mode 100644 index 00000000..563bbdfe --- /dev/null +++ b/src/jsc/jsc_context.hpp @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +inline JSGlobalContextRef jsc::Context::get_global_context(JSContextRef ctx) { + return JSContextGetGlobalContext(ctx); +} + +} // js +} // realm diff --git a/src/jsc/jsc_exception.hpp b/src/jsc/jsc_exception.hpp new file mode 100644 index 00000000..c6b315ac --- /dev/null +++ b/src/jsc/jsc_exception.hpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +inline JSValueRef jsc::Exception::value(JSContextRef ctx, const std::string &message) { + JSValueRef value = jsc::Value::from_string(ctx, message); + return JSObjectMakeError(ctx, 1, &value, NULL); +} + +} // js +} // realm diff --git a/src/jsc/jsc_function.hpp b/src/jsc/jsc_function.hpp new file mode 100644 index 00000000..866c973f --- /dev/null +++ b/src/jsc/jsc_function.hpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, size_t argc, const JSValueRef arguments[]) { + JSValueRef exception = nullptr; + JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return result; +} + +template<> +inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, size_t argc, const JSValueRef arguments[]) { + JSValueRef exception = nullptr; + JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return result; +} + +} // js +} // realm diff --git a/src/jsc/jsc_init.hpp b/src/jsc/jsc_init.hpp index c1621286..de19d7b9 100644 --- a/src/jsc/jsc_init.hpp +++ b/src/jsc/jsc_init.hpp @@ -19,5 +19,14 @@ #pragma once #include "jsc_init.h" +#include "jsc_string.hpp" +#include "jsc_protected.hpp" +#include "jsc_context.hpp" +#include "jsc_value.hpp" +#include "jsc_object.hpp" +#include "jsc_function.hpp" +#include "jsc_exception.hpp" +#include "jsc_return_value.hpp" #include "jsc_object_accessor.hpp" + #include "js_realm.hpp" diff --git a/src/jsc/jsc_object.hpp b/src/jsc/jsc_object.hpp new file mode 100644 index 00000000..0013add0 --- /dev/null +++ b/src/jsc/jsc_object.hpp @@ -0,0 +1,147 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { + return JSObjectHasProperty(ctx, object, key); +} + +template<> +inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { + return JSObjectHasProperty(ctx, object, jsc::String(util::to_string(index))); +} + +template<> +inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { + JSValueRef exception = nullptr; + JSValueRef value = JSObjectGetProperty(ctx, object, key, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return value; +} + +template<> +inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { + JSValueRef exception = nullptr; + JSValueRef value = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return value; +} + +template<> +inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key, const JSValueRef &value, PropertyAttributes attributes) { + JSValueRef exception = nullptr; + JSObjectSetProperty(ctx, object, key, value, attributes << 1, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } +} + +template<> +inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index, const JSValueRef &value) { + JSValueRef exception = nullptr; + JSObjectSetPropertyAtIndex(ctx, object, index, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } +} + +template<> +inline std::vector jsc::Object::get_property_names(JSContextRef ctx, const JSObjectRef &object) { + JSPropertyNameArrayRef property_names = JSObjectCopyPropertyNames(ctx, object); + size_t property_count = JSPropertyNameArrayGetCount(property_names); + + std::vector names; + names.reserve(property_count); + + for (size_t i = 0; i < property_count; i++) { + names.push_back(JSPropertyNameArrayGetNameAtIndex(property_names, i)); + } + + JSPropertyNameArrayRelease(property_names); + return names; +} + +template<> +inline JSValueRef jsc::Object::get_prototype(JSContextRef ctx, const JSObjectRef &object) { + return JSObjectGetPrototype(ctx, object); +} + +template<> +inline void jsc::Object::set_prototype(JSContextRef ctx, const JSObjectRef &object, const JSValueRef &prototype) { + JSObjectSetPrototype(ctx, object, prototype); +} + +template<> +inline JSObjectRef jsc::Object::create_empty(JSContextRef ctx) { + return JSObjectMake(ctx, nullptr, nullptr); +} + +template<> +inline JSObjectRef jsc::Object::create_array(JSContextRef ctx, uint32_t length, const JSValueRef values[]) { + JSValueRef exception = nullptr; + JSObjectRef array = JSObjectMakeArray(ctx, length, values, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return array; +} + +template<> +inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) { + JSValueRef number = jsc::Value::from_number(ctx, time); + return JSObjectMakeDate(ctx, 1, &number, nullptr); +} + +template<> +template +inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, typename ClassType::Internal* internal) { + return jsc::ObjectWrap::create_instance(ctx, internal); +} + +template<> +template +inline bool jsc::Object::is_instance(JSContextRef ctx, const JSObjectRef &object) { + return jsc::ObjectWrap::has_instance(ctx, object); +} + +template<> +template +inline typename ClassType::Internal* jsc::Object::get_internal(const JSObjectRef &object) { + return *static_cast *>(JSObjectGetPrivate(object)); +} + +template<> +template +inline void jsc::Object::set_internal(const JSObjectRef &object, typename ClassType::Internal* ptr) { + auto wrap = static_cast *>(JSObjectGetPrivate(object)); + *wrap = ptr; +} + +} // js +} // realm diff --git a/src/jsc/jsc_protected.hpp b/src/jsc/jsc_protected.hpp new file mode 100644 index 00000000..79f2b020 --- /dev/null +++ b/src/jsc/jsc_protected.hpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace jsc { + +template +class Protected { + const MemberType m_value; + + public: + Protected(MemberType value) : m_value(value) {} + + operator MemberType() const { + return m_value; + } +}; + +} // jsc + +namespace js { + +template<> +class Protected : public jsc::Protected { + public: + Protected(JSGlobalContextRef ctx) : jsc::Protected(ctx) { + JSGlobalContextRetain(*this); + } + ~Protected() { + JSGlobalContextRelease(*this); + } +}; + +template<> +class Protected : public jsc::Protected { + const JSGlobalContextRef m_context; + + public: + Protected(JSContextRef ctx, JSValueRef value) : jsc::Protected(value), m_context(JSContextGetGlobalContext(ctx)) { + JSValueProtect(m_context, *this); + } + ~Protected() { + JSValueUnprotect(m_context, *this); + } +}; + +template<> +class Protected : public Protected { + public: + Protected(JSContextRef ctx, JSObjectRef object) : Protected(ctx, object) {} + + operator JSObjectRef() const { + JSValueRef value = static_cast(*this); + return (JSObjectRef)value; + } +}; + +} // js +} // realm diff --git a/src/jsc/jsc_return_value.hpp b/src/jsc/jsc_return_value.hpp new file mode 100644 index 00000000..1974e384 --- /dev/null +++ b/src/jsc/jsc_return_value.hpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +class ReturnValue { + const JSContextRef m_context; + JSValueRef m_value = nullptr; + + public: + ReturnValue(JSContextRef ctx) : m_context(ctx) {} + + void set(const JSValueRef &value) { + m_value = value; + } + void set(const std::string &string) { + m_value = JSValueMakeString(m_context, jsc::String(string)); + } + void set(bool boolean) { + m_value = JSValueMakeBoolean(m_context, boolean); + } + void set(double number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set(int32_t number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set(uint32_t number) { + m_value = JSValueMakeNumber(m_context, number); + } + void set_null() { + m_value = JSValueMakeNull(m_context); + } + void set_undefined() { + m_value = JSValueMakeUndefined(m_context); + } + operator JSValueRef() const { + return m_value; + } +}; + +} // js +} // realm diff --git a/src/jsc/jsc_string.hpp b/src/jsc/jsc_string.hpp new file mode 100644 index 00000000..521b5c70 --- /dev/null +++ b/src/jsc/jsc_string.hpp @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +template<> +class String { + using StringType = String; + + JSStringRef m_str; + + public: + String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {} + String(const JSStringRef &s) : m_str(JSStringRetain(s)) {} + String(const std::string &str) : String(str.c_str()) {} + String(const StringType &o) : String(o.m_str) {} + String(StringType &&o) : m_str(o.m_str) { + o.m_str = nullptr; + } + ~String() { + if (m_str) { + JSStringRelease(m_str); + } + } + + operator JSStringRef() const { + return m_str; + } + operator std::string() const { + size_t max_size = JSStringGetMaximumUTF8CStringSize(m_str); + std::string string; + string.resize(max_size); + string.resize(JSStringGetUTF8CString(m_str, &string[0], max_size) - 1); + return string; + } +}; + +} // js +} // realm diff --git a/src/jsc/jsc_types.hpp b/src/jsc/jsc_types.hpp index 835f0f96..07c36e48 100644 --- a/src/jsc/jsc_types.hpp +++ b/src/jsc/jsc_types.hpp @@ -47,19 +47,7 @@ struct Types { using StringPropertyEnumeratorCallback = JSObjectGetPropertyNamesCallback; }; -template -class Protected { - const T m_value; - - public: - Protected(T value) : m_value(value) {} - - operator T() const { - return m_value; - } -}; - -template +template class ObjectWrap; using String = js::String; @@ -71,430 +59,4 @@ using Exception = js::Exception; using ReturnValue = js::ReturnValue; } // jsc - -namespace js { - -template<> -class String { - using StringType = String; - - JSStringRef m_str; - - public: - String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {} - String(const JSStringRef &s) : m_str(JSStringRetain(s)) {} - String(const std::string &str) : String(str.c_str()) {} - String(const StringType &o) : String(o.m_str) {} - String(StringType &&o) : m_str(o.m_str) { - o.m_str = nullptr; - } - ~String() { - if (m_str) { - JSStringRelease(m_str); - } - } - - operator JSStringRef() const { - return m_str; - } - operator std::string() const { - size_t max_size = JSStringGetMaximumUTF8CStringSize(m_str); - std::string string; - string.resize(max_size); - string.resize(JSStringGetUTF8CString(m_str, &string[0], max_size) - 1); - return string; - } -}; - -template<> -class ReturnValue { - const JSContextRef m_context; - JSValueRef m_value = nullptr; - - public: - ReturnValue(JSContextRef ctx) : m_context(ctx) {} - - void set(const JSValueRef &value) { - m_value = value; - } - void set(const std::string &string) { - m_value = JSValueMakeString(m_context, jsc::String(string)); - } - void set(bool boolean) { - m_value = JSValueMakeBoolean(m_context, boolean); - } - void set(double number) { - m_value = JSValueMakeNumber(m_context, number); - } - void set(int32_t number) { - m_value = JSValueMakeNumber(m_context, number); - } - void set(uint32_t number) { - m_value = JSValueMakeNumber(m_context, number); - } - void set_null() { - m_value = JSValueMakeNull(m_context); - } - void set_undefined() { - m_value = JSValueMakeUndefined(m_context); - } - operator JSValueRef() const { - return m_value; - } -}; - -template<> -class Protected : public jsc::Protected { - public: - Protected(JSGlobalContextRef ctx) : jsc::Protected(ctx) { - JSGlobalContextRetain(*this); - } - ~Protected() { - JSGlobalContextRelease(*this); - } -}; - -template<> -class Protected : public jsc::Protected { - const JSGlobalContextRef m_context; - - public: - Protected(JSContextRef ctx, JSValueRef value) : jsc::Protected(value), m_context(JSContextGetGlobalContext(ctx)) { - JSValueProtect(m_context, *this); - } - ~Protected() { - JSValueUnprotect(m_context, *this); - } -}; - -template<> -class Protected : public Protected { - public: - Protected(JSContextRef ctx, JSObjectRef object) : Protected(ctx, object) {} - - operator JSObjectRef() const { - JSValueRef value = static_cast(*this); - return (JSObjectRef)value; - } -}; - -static inline bool is_object_of_type(JSContextRef ctx, JSValueRef value, jsc::String type) { - JSObjectRef global_object = JSContextGetGlobalObject(ctx); - JSValueRef exception = nullptr; - JSValueRef constructor = JSObjectGetProperty(ctx, global_object, type, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - - bool result = JSValueIsInstanceOfConstructor(ctx, value, jsc::Value::validated_to_constructor(ctx, constructor), &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - - return result; -} - -template<> -inline JSGlobalContextRef jsc::Context::get_global_context(JSContextRef ctx) { - return JSContextGetGlobalContext(ctx); -} - -template<> -inline bool jsc::Value::is_array(JSContextRef ctx, const JSValueRef &value) { - // JSValueIsArray() is not available until iOS 9. - static const jsc::String type = "Array"; - return is_object_of_type(ctx, value, type); -} - -template<> -inline bool jsc::Value::is_array_buffer(JSContextRef ctx, const JSValueRef &value) { - static const jsc::String type = "ArrayBuffer"; - return is_object_of_type(ctx, value, type); -} - -template<> -inline bool jsc::Value::is_date(JSContextRef ctx, const JSValueRef &value) { - static const jsc::String type = "Date"; - return is_object_of_type(ctx, value, type); -} - -template<> -inline bool jsc::Value::is_boolean(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsBoolean(ctx, value); -} - -template<> -inline bool jsc::Value::is_constructor(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); -} - -template<> -inline bool jsc::Value::is_function(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsObject(ctx, value) && JSObjectIsFunction(ctx, (JSObjectRef)value); -} - -template<> -inline bool jsc::Value::is_null(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsNull(ctx, value); -} - -template<> -inline bool jsc::Value::is_number(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsNumber(ctx, value); -} - -template<> -inline bool jsc::Value::is_object(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsObject(ctx, value); -} - -template<> -inline bool jsc::Value::is_string(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsString(ctx, value); -} - -template<> -inline bool jsc::Value::is_undefined(JSContextRef ctx, const JSValueRef &value) { - return JSValueIsUndefined(ctx, value); -} - -template<> -inline bool jsc::Value::is_valid(const JSValueRef &value) { - return value != nullptr; -} - -template<> -inline JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) { - return JSValueMakeBoolean(ctx, boolean); -} - -template<> -inline JSValueRef jsc::Value::from_null(JSContextRef ctx) { - return JSValueMakeNull(ctx); -} - -template<> -inline JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) { - return JSValueMakeNumber(ctx, number); -} - -template<> -inline JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) { - return JSValueMakeString(ctx, string); -} - -template<> -inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) { - return JSValueMakeUndefined(ctx); -} - -template<> -inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) { - return JSValueToBoolean(ctx, value); -} - -template<> -inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) { - JSValueRef exception = nullptr; - double number = JSValueToNumber(ctx, value, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - if (isnan(number)) { - throw std::invalid_argument("Value not convertible to a number."); - } - return number; -} - -template<> -inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) { - JSValueRef exception = nullptr; - jsc::String string = JSValueToStringCopy(ctx, value, &exception); - - // Since the string's retain value is +2 here, we need to manually release it before returning. - JSStringRelease(string); - - if (exception) { - throw jsc::Exception(ctx, exception); - } - return string; -} - -template<> -inline JSObjectRef jsc::Value::to_object(JSContextRef ctx, const JSValueRef &value) { - JSValueRef exception = nullptr; - JSObjectRef object = JSValueToObject(ctx, value, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return object; -} - -template<> -inline JSObjectRef jsc::Value::to_array(JSContextRef ctx, const JSValueRef &value) { - return to_object(ctx, value); -} - -template<> -inline JSObjectRef jsc::Value::to_constructor(JSContextRef ctx, const JSValueRef &value) { - return to_object(ctx, value); -} - -template<> -inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) { - return to_object(ctx, value); -} - -template<> -inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &value) { - return to_object(ctx, value); -} - -template<> -inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, size_t argc, const JSValueRef arguments[]) { - JSValueRef exception = nullptr; - JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return result; -} - -template<> -inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, size_t argc, const JSValueRef arguments[]) { - JSValueRef exception = nullptr; - JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return result; -} - -template<> -inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { - return JSObjectHasProperty(ctx, object, key); -} - -template<> -inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { - return JSObjectHasProperty(ctx, object, jsc::String(util::to_string(index))); -} - -template<> -inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) { - JSValueRef exception = nullptr; - JSValueRef value = JSObjectGetProperty(ctx, object, key, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return value; -} - -template<> -inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) { - JSValueRef exception = nullptr; - JSValueRef value = JSObjectGetPropertyAtIndex(ctx, object, index, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return value; -} - -template<> -inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key, const JSValueRef &value, PropertyAttributes attributes) { - JSValueRef exception = nullptr; - JSObjectSetProperty(ctx, object, key, value, attributes << 1, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } -} - -template<> -inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index, const JSValueRef &value) { - JSValueRef exception = nullptr; - JSObjectSetPropertyAtIndex(ctx, object, index, value, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } -} - -template<> -inline std::vector jsc::Object::get_property_names(JSContextRef ctx, const JSObjectRef &object) { - JSPropertyNameArrayRef property_names = JSObjectCopyPropertyNames(ctx, object); - size_t property_count = JSPropertyNameArrayGetCount(property_names); - - std::vector names; - names.reserve(property_count); - - for (size_t i = 0; i < property_count; i++) { - names.push_back(JSPropertyNameArrayGetNameAtIndex(property_names, i)); - } - - JSPropertyNameArrayRelease(property_names); - return names; -} - -template<> -inline JSValueRef jsc::Object::get_prototype(JSContextRef ctx, const JSObjectRef &object) { - return JSObjectGetPrototype(ctx, object); -} - -template<> -inline void jsc::Object::set_prototype(JSContextRef ctx, const JSObjectRef &object, const JSValueRef &prototype) { - JSObjectSetPrototype(ctx, object, prototype); -} - -template<> -inline JSObjectRef jsc::Object::create_empty(JSContextRef ctx) { - return JSObjectMake(ctx, nullptr, nullptr); -} - -template<> -inline JSObjectRef jsc::Object::create_array(JSContextRef ctx, uint32_t length, const JSValueRef values[]) { - JSValueRef exception = nullptr; - JSObjectRef array = JSObjectMakeArray(ctx, length, values, &exception); - if (exception) { - throw jsc::Exception(ctx, exception); - } - return array; -} - -template<> -inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) { - JSValueRef number = jsc::Value::from_number(ctx, time); - return JSObjectMakeDate(ctx, 1, &number, nullptr); -} - -template<> -template -inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, typename ClassType::Internal* internal) { - return jsc::ObjectWrap::create_instance(ctx, internal); -} - -template<> -template -inline bool jsc::Object::is_instance(JSContextRef ctx, const JSObjectRef &object) { - return jsc::ObjectWrap::has_instance(ctx, object); -} - -template<> -template -inline typename ClassType::Internal* jsc::Object::get_internal(const JSObjectRef &object) { - return *static_cast *>(JSObjectGetPrivate(object)); -} - -template<> -template -inline void jsc::Object::set_internal(const JSObjectRef &object, typename ClassType::Internal* ptr) { - auto wrap = static_cast *>(JSObjectGetPrivate(object)); - *wrap = ptr; -} - -template<> -inline JSValueRef jsc::Exception::value(JSContextRef ctx, const std::string &message) { - JSValueRef value = jsc::Value::from_string(ctx, message); - return JSObjectMakeError(ctx, 1, &value, NULL); -} - -} // js } // realm diff --git a/src/jsc/jsc_value.hpp b/src/jsc/jsc_value.hpp new file mode 100644 index 00000000..d2df79c8 --- /dev/null +++ b/src/jsc/jsc_value.hpp @@ -0,0 +1,194 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "jsc_types.hpp" + +namespace realm { +namespace js { + +static inline bool is_object_of_type(JSContextRef ctx, JSValueRef value, jsc::String type) { + JSObjectRef global_object = JSContextGetGlobalObject(ctx); + JSValueRef exception = nullptr; + JSValueRef constructor = JSObjectGetProperty(ctx, global_object, type, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + + bool result = JSValueIsInstanceOfConstructor(ctx, value, jsc::Value::validated_to_constructor(ctx, constructor), &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + + return result; +} + +template<> +inline bool jsc::Value::is_array(JSContextRef ctx, const JSValueRef &value) { + // JSValueIsArray() is not available until iOS 9. + static const jsc::String type = "Array"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_array_buffer(JSContextRef ctx, const JSValueRef &value) { + static const jsc::String type = "ArrayBuffer"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_date(JSContextRef ctx, const JSValueRef &value) { + static const jsc::String type = "Date"; + return is_object_of_type(ctx, value, type); +} + +template<> +inline bool jsc::Value::is_boolean(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsBoolean(ctx, value); +} + +template<> +inline bool jsc::Value::is_constructor(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value); +} + +template<> +inline bool jsc::Value::is_function(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value) && JSObjectIsFunction(ctx, (JSObjectRef)value); +} + +template<> +inline bool jsc::Value::is_null(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsNull(ctx, value); +} + +template<> +inline bool jsc::Value::is_number(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsNumber(ctx, value); +} + +template<> +inline bool jsc::Value::is_object(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsObject(ctx, value); +} + +template<> +inline bool jsc::Value::is_string(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsString(ctx, value); +} + +template<> +inline bool jsc::Value::is_undefined(JSContextRef ctx, const JSValueRef &value) { + return JSValueIsUndefined(ctx, value); +} + +template<> +inline bool jsc::Value::is_valid(const JSValueRef &value) { + return value != nullptr; +} + +template<> +inline JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) { + return JSValueMakeBoolean(ctx, boolean); +} + +template<> +inline JSValueRef jsc::Value::from_null(JSContextRef ctx) { + return JSValueMakeNull(ctx); +} + +template<> +inline JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) { + return JSValueMakeNumber(ctx, number); +} + +template<> +inline JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) { + return JSValueMakeString(ctx, string); +} + +template<> +inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) { + return JSValueMakeUndefined(ctx); +} + +template<> +inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) { + return JSValueToBoolean(ctx, value); +} + +template<> +inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + double number = JSValueToNumber(ctx, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + if (isnan(number)) { + throw std::invalid_argument("Value not convertible to a number."); + } + return number; +} + +template<> +inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + jsc::String string = JSValueToStringCopy(ctx, value, &exception); + + // Since the string's retain value is +2 here, we need to manually release it before returning. + JSStringRelease(string); + + if (exception) { + throw jsc::Exception(ctx, exception); + } + return string; +} + +template<> +inline JSObjectRef jsc::Value::to_object(JSContextRef ctx, const JSValueRef &value) { + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(ctx, value, &exception); + if (exception) { + throw jsc::Exception(ctx, exception); + } + return object; +} + +template<> +inline JSObjectRef jsc::Value::to_array(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_constructor(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +template<> +inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &value) { + return to_object(ctx, value); +} + +} // js +} // realm diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 5c54e95e..bc5e0214 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -19,6 +19,7 @@ #pragma once #include "node_types.hpp" + #include "js_class.hpp" #include "js_util.hpp" diff --git a/src/node/node_context.hpp b/src/node/node_context.hpp new file mode 100644 index 00000000..b77b1b64 --- /dev/null +++ b/src/node/node_context.hpp @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +inline v8::Local node::Context::get_global_context(v8::Isolate* isolate) { + return isolate->GetCurrentContext(); +} + +} // js +} // realm diff --git a/src/node/node_exception.hpp b/src/node/node_exception.hpp new file mode 100644 index 00000000..17eca27f --- /dev/null +++ b/src/node/node_exception.hpp @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +inline v8::Local node::Exception::value(v8::Isolate* isolate, const std::string &message) { + return Nan::Error(message.c_str()); +} + +} // js +} // realm diff --git a/src/node/node_function.hpp b/src/node/node_function.hpp new file mode 100644 index 00000000..f983ef23 --- /dev/null +++ b/src/node/node_function.hpp @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +inline v8::Local node::Function::call(v8::Isolate* isolate, const v8::Local &function, const v8::Local &this_object, size_t argc, const v8::Local arguments[]) { + Nan::TryCatch trycatch; + auto result = Nan::Call(function, this_object, (int)argc, const_cast*>(arguments)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return result.ToLocalChecked(); +} + +template<> +inline v8::Local node::Function::construct(v8::Isolate* isolate, const v8::Local &function, size_t argc, const v8::Local arguments[]) { + Nan::TryCatch trycatch; + auto result = Nan::NewInstance(function, (int)argc, const_cast*>(arguments)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return result.ToLocalChecked(); +} + +} // js +} // realm diff --git a/src/node/node_init.hpp b/src/node/node_init.hpp index 8a921da4..06980428 100644 --- a/src/node/node_init.hpp +++ b/src/node/node_init.hpp @@ -18,5 +18,14 @@ #pragma once +#include "node_string.hpp" +#include "node_protected.hpp" +#include "node_context.hpp" +#include "node_value.hpp" +#include "node_object.hpp" +#include "node_function.hpp" +#include "node_exception.hpp" +#include "node_return_value.hpp" #include "node_object_accessor.hpp" + #include "js_realm.hpp" diff --git a/src/node/node_object.hpp b/src/node/node_object.hpp new file mode 100644 index 00000000..c1fa9dd0 --- /dev/null +++ b/src/node/node_object.hpp @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { + return Nan::Has(object, key).FromMaybe(false); +} + +template<> +inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { + return Nan::Has(object, index).FromMaybe(false); +} + +template<> +inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { + Nan::TryCatch trycatch; + auto value = Nan::Get(object, v8::Local(key)); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return value.ToLocalChecked(); +} + +template<> +inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { + Nan::TryCatch trycatch; + auto value = Nan::Get(object, index); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } + return value.ToLocalChecked(); +} + +template<> +inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key, const v8::Local &value, PropertyAttributes attributes) { + Nan::TryCatch trycatch; + + if (attributes) { + Nan::ForceSet(object, v8::Local(key), value, v8::PropertyAttribute(attributes)); + } + else { + Nan::Set(object, v8::Local(key), value); + } + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } +} + +template<> +inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index, const v8::Local &value) { + Nan::TryCatch trycatch; + Nan::Set(object, index, value); + + if (trycatch.HasCaught()) { + throw node::Exception(isolate, trycatch.Exception()); + } +} + +template<> +inline std::vector node::Object::get_property_names(v8::Isolate* isolate, const v8::Local &object) { + auto maybe_array = Nan::GetPropertyNames(object); + if (maybe_array.IsEmpty()) { + return std::vector(); + } + + auto array = maybe_array.ToLocalChecked(); + uint32_t count = array->Length(); + + std::vector names; + names.reserve(count); + + for (uint32_t i = 0; i < count; i++) { + names.push_back(array->Get(i)->ToString()); + } + + return names; +} + +template<> +inline v8::Local node::Object::get_prototype(v8::Isolate* isolate, const v8::Local &object) { + return object->GetPrototype(); +} + +template<> +inline void node::Object::set_prototype(v8::Isolate* isolate, const v8::Local &object, const v8::Local &prototype) { + Nan::SetPrototype(object, prototype); +} + +template<> +inline v8::Local node::Object::create_empty(v8::Isolate* isolate) { + return Nan::New(); +} + +template<> +inline v8::Local node::Object::create_array(v8::Isolate* isolate, uint32_t length, const v8::Local values[]) { + v8::Local array = Nan::New(length); + for (uint32_t i = 0; i < length; i++) { + set_property(isolate, array, i, values[i]); + } + return array; +} + +template<> +inline v8::Local node::Object::create_date(v8::Isolate* isolate, double time) { + return Nan::New(time).ToLocalChecked(); +} + +template<> +template +inline v8::Local node::Object::create_instance(v8::Isolate* isolate, typename ClassType::Internal* internal) { + return node::ObjectWrap::create_instance(isolate, internal); +} + +template<> +template +inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local &object) { + return node::ObjectWrap::has_instance(isolate, object); +} + +template<> +template +inline typename ClassType::Internal* node::Object::get_internal(const v8::Local &object) { + return *Nan::ObjectWrap::Unwrap>(object); +} + +template<> +template +inline void node::Object::set_internal(const v8::Local &object, typename ClassType::Internal* ptr) { + auto wrap = Nan::ObjectWrap::Unwrap>(object); + *wrap = ptr; +} + +} // js +} // realm diff --git a/src/node/node_protected.hpp b/src/node/node_protected.hpp new file mode 100644 index 00000000..7ae354f1 --- /dev/null +++ b/src/node/node_protected.hpp @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace node { + +template +class Protected { + // TODO: Figure out why Nan::CopyablePersistentTraits causes a build failure. + Nan::Persistent> m_value; + + public: + Protected(v8::Local value) : m_value(value) {} + + operator v8::Local() const { + return Nan::New(m_value); + } + bool operator==(const v8::Local &other) const { + return m_value == other; + } + bool operator!=(const v8::Local &other) const { + return m_value != other; + } + bool operator==(const Protected &other) const { + return m_value == other.m_value; + } + bool operator!=(const Protected &other) const { + return m_value != other.m_value; + } +}; + +} // node + +namespace js { + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Local ctx) : node::Protected(ctx) {} + + operator v8::Isolate*() const { + return v8::Local(*this)->GetIsolate(); + } +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local value) : node::Protected(value) {} +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} +}; + +template<> +class Protected : public node::Protected { + public: + Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} +}; + +} // js +} // realm diff --git a/src/node/node_return_value.hpp b/src/node/node_return_value.hpp new file mode 100644 index 00000000..310cc854 --- /dev/null +++ b/src/node/node_return_value.hpp @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +class ReturnValue { + Nan::ReturnValue m_value; + + public: + ReturnValue(Nan::ReturnValue value) : m_value(value) {} + + void set(const v8::Local &value) { + m_value.Set(value); + } + void set(const std::string &string) { + if (string.empty()) { + m_value.SetEmptyString(); + } + else { + m_value.Set(Nan::New(string).ToLocalChecked()); + } + } + void set(bool boolean) { + m_value.Set(boolean); + } + void set(double number) { + m_value.Set(number); + } + void set(int32_t number) { + m_value.Set(number); + } + void set(uint32_t number) { + m_value.Set(number); + } + void set_null() { + m_value.SetNull(); + } + void set_undefined() { + m_value.SetUndefined(); + } +}; + +} // js +} // realm diff --git a/src/node/node_string.hpp b/src/node/node_string.hpp new file mode 100644 index 00000000..dabe9d07 --- /dev/null +++ b/src/node/node_string.hpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +class String { + std::string m_str; + + public: + String(const char* s) : m_str(s) {} + String(const std::string &s) : m_str(s) {} + String(const v8::Local &s) : m_str(*Nan::Utf8String(s)) {} + String(v8::Local &&s) : String(s) {} + + operator std::string() const { + return m_str; + } + operator v8::Local() const { + return Nan::New(m_str).ToLocalChecked(); + } +}; + +} // js +} // realm diff --git a/src/node/node_types.hpp b/src/node/node_types.hpp index d99343ac..4220c173 100644 --- a/src/node/node_types.hpp +++ b/src/node/node_types.hpp @@ -53,32 +53,7 @@ struct Types { using StringPropertyEnumeratorCallback = Nan::PropertyEnumeratorCallback; }; -template -class Protected { - // TODO: Figure out why Nan::CopyablePersistentTraits causes a build failure. - Nan::Persistent> m_value; - - public: - Protected(v8::Local value) : m_value(value) {} - - operator v8::Local() const { - return Nan::New(m_value); - } - bool operator==(const v8::Local &other) const { - return m_value == other; - } - bool operator!=(const v8::Local &other) const { - return m_value != other; - } - bool operator==(const Protected &other) const { - return m_value == other.m_value; - } - bool operator!=(const Protected &other) const { - return m_value != other.m_value; - } -}; - -template +template class ObjectWrap; using String = js::String; @@ -90,398 +65,4 @@ using Exception = js::Exception; using ReturnValue = js::ReturnValue; } // node - -namespace js { - -template<> -class String { - std::string m_str; - - public: - String(const char* s) : m_str(s) {} - String(const std::string &s) : m_str(s) {} - String(const v8::Local &s) : m_str(*Nan::Utf8String(s)) {} - String(v8::Local &&s) : String(s) {} - - operator std::string() const { - return m_str; - } - operator v8::Local() const { - return Nan::New(m_str).ToLocalChecked(); - } -}; - -template<> -class ReturnValue { - Nan::ReturnValue m_value; - - public: - ReturnValue(Nan::ReturnValue value) : m_value(value) {} - - void set(const v8::Local &value) { - m_value.Set(value); - } - void set(const std::string &string) { - if (string.empty()) { - m_value.SetEmptyString(); - } - else { - m_value.Set(Nan::New(string).ToLocalChecked()); - } - } - void set(bool boolean) { - m_value.Set(boolean); - } - void set(double number) { - m_value.Set(number); - } - void set(int32_t number) { - m_value.Set(number); - } - void set(uint32_t number) { - m_value.Set(number); - } - void set_null() { - m_value.SetNull(); - } - void set_undefined() { - m_value.SetUndefined(); - } -}; - -template<> -class Protected : public node::Protected { - public: - Protected(v8::Local ctx) : node::Protected(ctx) {} - - operator v8::Isolate*() const { - return v8::Local(*this)->GetIsolate(); - } -}; - -template<> -class Protected : public node::Protected { - public: - Protected(v8::Isolate* isolate, v8::Local value) : node::Protected(value) {} -}; - -template<> -class Protected : public node::Protected { - public: - Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} -}; - -template<> -class Protected : public node::Protected { - public: - Protected(v8::Isolate* isolate, v8::Local object) : node::Protected(object) {} -}; - -template<> -inline v8::Local node::Context::get_global_context(v8::Isolate* isolate) { - return isolate->GetCurrentContext(); -} - -template<> -inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local &value) { - return value->IsArray(); -} - -template<> -inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local &value) { -#if REALM_V8_ARRAY_BUFFER_API - return value->IsArrayBuffer(); -#else - // TODO: Implement this! -#endif -} - -template<> -inline bool node::Value::is_array_buffer_view(v8::Isolate* isolate, const v8::Local &value) { -#if REALM_V8_ARRAY_BUFFER_API - return value->IsArrayBufferView(); -#else - // TODO: Implement this! -#endif -} - -template<> -inline bool node::Value::is_date(v8::Isolate* isolate, const v8::Local &value) { - return value->IsDate(); -} - -template<> -inline bool node::Value::is_boolean(v8::Isolate* isolate, const v8::Local &value) { - return value->IsBoolean(); -} - -template<> -inline bool node::Value::is_constructor(v8::Isolate* isolate, const v8::Local &value) { - return value->IsFunction(); -} - -template<> -inline bool node::Value::is_function(v8::Isolate* isolate, const v8::Local &value) { - return value->IsFunction(); -} - -template<> -inline bool node::Value::is_null(v8::Isolate* isolate, const v8::Local &value) { - return value->IsNull(); -} - -template<> -inline bool node::Value::is_number(v8::Isolate* isolate, const v8::Local &value) { - return value->IsNumber(); -} - -template<> -inline bool node::Value::is_object(v8::Isolate* isolate, const v8::Local &value) { - return value->IsObject(); -} - -template<> -inline bool node::Value::is_string(v8::Isolate* isolate, const v8::Local &value) { - return value->IsString(); -} - -template<> -inline bool node::Value::is_undefined(v8::Isolate* isolate, const v8::Local &value) { - return value->IsUndefined(); -} - -template<> -inline bool node::Value::is_valid(const v8::Local &value) { - return !value.IsEmpty(); -} - -template<> -inline v8::Local node::Value::from_boolean(v8::Isolate* isolate, bool boolean) { - return Nan::New(boolean); -} - -template<> -inline v8::Local node::Value::from_null(v8::Isolate* isolate) { - return Nan::Null(); -} - -template<> -inline v8::Local node::Value::from_number(v8::Isolate* isolate, double number) { - return Nan::New(number); -} - -template<> -inline v8::Local node::Value::from_string(v8::Isolate* isolate, const node::String &string) { - return v8::Local(string); -} - -template<> -inline v8::Local node::Value::from_undefined(v8::Isolate* isolate) { - return Nan::Undefined(); -} - -template<> -inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local &value) { - return Nan::To(value).FromMaybe(false); -} - -template<> -inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local &value) { - double number = Nan::To(value).FromMaybe(NAN); - if (isnan(number)) { - throw std::invalid_argument("Value not convertible to a number."); - } - return number; -} - -template<> -inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local &value) { - return value->ToString(); -} - -template<> -inline v8::Local node::Value::to_object(v8::Isolate* isolate, const v8::Local &value) { - return Nan::To(value).FromMaybe(v8::Local()); -} - -template<> -inline v8::Local node::Value::to_array(v8::Isolate* isolate, const v8::Local &value) { - return to_object(isolate, value); -} - -template<> -inline v8::Local node::Value::to_date(v8::Isolate* isolate, const v8::Local &value) { - return to_object(isolate, value); -} - -template<> -inline v8::Local node::Value::to_function(v8::Isolate* isolate, const v8::Local &value) { - return value->IsFunction() ? v8::Local::Cast(value) : v8::Local(); -} - -template<> -inline v8::Local node::Value::to_constructor(v8::Isolate* isolate, const v8::Local &value) { - return to_function(isolate, value); -} - -template<> -inline v8::Local node::Function::call(v8::Isolate* isolate, const v8::Local &function, const v8::Local &this_object, size_t argc, const v8::Local arguments[]) { - Nan::TryCatch trycatch; - auto result = Nan::Call(function, this_object, (int)argc, const_cast*>(arguments)); - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } - return result.ToLocalChecked(); -} - -template<> -inline v8::Local node::Function::construct(v8::Isolate* isolate, const v8::Local &function, size_t argc, const v8::Local arguments[]) { - Nan::TryCatch trycatch; - auto result = Nan::NewInstance(function, (int)argc, const_cast*>(arguments)); - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } - return result.ToLocalChecked(); -} - -template<> -inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { - return Nan::Has(object, key).FromMaybe(false); -} - -template<> -inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { - return Nan::Has(object, index).FromMaybe(false); -} - -template<> -inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key) { - Nan::TryCatch trycatch; - auto value = Nan::Get(object, v8::Local(key)); - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } - return value.ToLocalChecked(); -} - -template<> -inline v8::Local node::Object::get_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index) { - Nan::TryCatch trycatch; - auto value = Nan::Get(object, index); - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } - return value.ToLocalChecked(); -} - -template<> -inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, const node::String &key, const v8::Local &value, PropertyAttributes attributes) { - Nan::TryCatch trycatch; - - if (attributes) { - Nan::ForceSet(object, v8::Local(key), value, v8::PropertyAttribute(attributes)); - } - else { - Nan::Set(object, v8::Local(key), value); - } - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } -} - -template<> -inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local &object, uint32_t index, const v8::Local &value) { - Nan::TryCatch trycatch; - Nan::Set(object, index, value); - - if (trycatch.HasCaught()) { - throw node::Exception(isolate, trycatch.Exception()); - } -} - -template<> -inline std::vector node::Object::get_property_names(v8::Isolate* isolate, const v8::Local &object) { - auto maybe_array = Nan::GetPropertyNames(object); - if (maybe_array.IsEmpty()) { - return std::vector(); - } - - auto array = maybe_array.ToLocalChecked(); - uint32_t count = array->Length(); - - std::vector names; - names.reserve(count); - - for (uint32_t i = 0; i < count; i++) { - names.push_back(array->Get(i)->ToString()); - } - - return names; -} - -template<> -inline v8::Local node::Object::get_prototype(v8::Isolate* isolate, const v8::Local &object) { - return object->GetPrototype(); -} - -template<> -inline void node::Object::set_prototype(v8::Isolate* isolate, const v8::Local &object, const v8::Local &prototype) { - Nan::SetPrototype(object, prototype); -} - -template<> -inline v8::Local node::Object::create_empty(v8::Isolate* isolate) { - return Nan::New(); -} - -template<> -inline v8::Local node::Object::create_array(v8::Isolate* isolate, uint32_t length, const v8::Local values[]) { - v8::Local array = Nan::New(length); - for (uint32_t i = 0; i < length; i++) { - set_property(isolate, array, i, values[i]); - } - return array; -} - -template<> -inline v8::Local node::Object::create_date(v8::Isolate* isolate, double time) { - return Nan::New(time).ToLocalChecked(); -} - -template<> -template -inline v8::Local node::Object::create_instance(v8::Isolate* isolate, typename ClassType::Internal* internal) { - return node::ObjectWrap::create_instance(isolate, internal); -} - -template<> -template -inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local &object) { - return node::ObjectWrap::has_instance(isolate, object); -} - -template<> -template -inline typename ClassType::Internal* node::Object::get_internal(const v8::Local &object) { - return *Nan::ObjectWrap::Unwrap>(object); -} - -template<> -template -inline void node::Object::set_internal(const v8::Local &object, typename ClassType::Internal* ptr) { - auto wrap = Nan::ObjectWrap::Unwrap>(object); - *wrap = ptr; -} - -template<> -inline v8::Local node::Exception::value(v8::Isolate* isolate, const std::string &message) { - return Nan::Error(message.c_str()); -} - -} // js } // realm diff --git a/src/node/node_value.hpp b/src/node/node_value.hpp new file mode 100644 index 00000000..0a25ad7f --- /dev/null +++ b/src/node/node_value.hpp @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "node_types.hpp" + +namespace realm { +namespace js { + +template<> +inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local &value) { + return value->IsArray(); +} + +template<> +inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local &value) { +#if REALM_V8_ARRAY_BUFFER_API + return value->IsArrayBuffer(); +#else + // TODO: Implement this! +#endif +} + +template<> +inline bool node::Value::is_array_buffer_view(v8::Isolate* isolate, const v8::Local &value) { +#if REALM_V8_ARRAY_BUFFER_API + return value->IsArrayBufferView(); +#else + // TODO: Implement this! +#endif +} + +template<> +inline bool node::Value::is_date(v8::Isolate* isolate, const v8::Local &value) { + return value->IsDate(); +} + +template<> +inline bool node::Value::is_boolean(v8::Isolate* isolate, const v8::Local &value) { + return value->IsBoolean(); +} + +template<> +inline bool node::Value::is_constructor(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction(); +} + +template<> +inline bool node::Value::is_function(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction(); +} + +template<> +inline bool node::Value::is_null(v8::Isolate* isolate, const v8::Local &value) { + return value->IsNull(); +} + +template<> +inline bool node::Value::is_number(v8::Isolate* isolate, const v8::Local &value) { + return value->IsNumber(); +} + +template<> +inline bool node::Value::is_object(v8::Isolate* isolate, const v8::Local &value) { + return value->IsObject(); +} + +template<> +inline bool node::Value::is_string(v8::Isolate* isolate, const v8::Local &value) { + return value->IsString(); +} + +template<> +inline bool node::Value::is_undefined(v8::Isolate* isolate, const v8::Local &value) { + return value->IsUndefined(); +} + +template<> +inline bool node::Value::is_valid(const v8::Local &value) { + return !value.IsEmpty(); +} + +template<> +inline v8::Local node::Value::from_boolean(v8::Isolate* isolate, bool boolean) { + return Nan::New(boolean); +} + +template<> +inline v8::Local node::Value::from_null(v8::Isolate* isolate) { + return Nan::Null(); +} + +template<> +inline v8::Local node::Value::from_number(v8::Isolate* isolate, double number) { + return Nan::New(number); +} + +template<> +inline v8::Local node::Value::from_string(v8::Isolate* isolate, const node::String &string) { + return v8::Local(string); +} + +template<> +inline v8::Local node::Value::from_undefined(v8::Isolate* isolate) { + return Nan::Undefined(); +} + +template<> +inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local &value) { + return Nan::To(value).FromMaybe(false); +} + +template<> +inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local &value) { + double number = Nan::To(value).FromMaybe(NAN); + if (isnan(number)) { + throw std::invalid_argument("Value not convertible to a number."); + } + return number; +} + +template<> +inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local &value) { + return value->ToString(); +} + +template<> +inline v8::Local node::Value::to_object(v8::Isolate* isolate, const v8::Local &value) { + return Nan::To(value).FromMaybe(v8::Local()); +} + +template<> +inline v8::Local node::Value::to_array(v8::Isolate* isolate, const v8::Local &value) { + return to_object(isolate, value); +} + +template<> +inline v8::Local node::Value::to_date(v8::Isolate* isolate, const v8::Local &value) { + return to_object(isolate, value); +} + +template<> +inline v8::Local node::Value::to_function(v8::Isolate* isolate, const v8::Local &value) { + return value->IsFunction() ? v8::Local::Cast(value) : v8::Local(); +} + +template<> +inline v8::Local node::Value::to_constructor(v8::Isolate* isolate, const v8::Local &value) { + return to_function(isolate, value); +} + +} // js +} // realm From 49e2b541517b57fe89759594e5284de37d5704d2 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 12:07:36 -0700 Subject: [PATCH 32/48] Convert some more camelCase in js_realm.hpp --- src/js_realm.hpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index a893cb51..83d713b7 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -259,32 +259,32 @@ void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const else if (Value::is_object(ctx, value)) { TObject object = Value::validated_to_object(ctx, value); - TValue pathValue = Object::get_property(ctx, object, path_string); - if (!Value::is_undefined(ctx, pathValue)) { - config.path = Value::validated_to_string(ctx, pathValue, "path"); + TValue path_value = Object::get_property(ctx, object, path_string); + if (!Value::is_undefined(ctx, path_value)) { + config.path = Value::validated_to_string(ctx, path_value, "path"); } else { config.path = js::default_path(); } - TValue schemaValue = Object::get_property(ctx, object, schema_string); - if (!Value::is_undefined(ctx, schemaValue)) { - TObject schemaObject = Value::validated_to_object(ctx, schemaValue, "schema"); - config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, schemaObject, defaults, constructors))); + TValue schema_value = Object::get_property(ctx, object, schema_string); + if (!Value::is_undefined(ctx, schema_value)) { + TObject schema_object = Value::validated_to_object(ctx, schema_value, "schema"); + config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, schema_object, defaults, constructors))); } - TValue versionValue = Object::get_property(ctx, object, schema_version_string); - if (!Value::is_undefined(ctx, versionValue)) { - config.schema_version = Value::validated_to_number(ctx, versionValue, "schemaVersion"); + TValue version_value = Object::get_property(ctx, object, schema_version_string); + if (!Value::is_undefined(ctx, version_value)) { + config.schema_version = Value::validated_to_number(ctx, version_value, "schemaVersion"); } else { config.schema_version = 0; } - TValue encryptionKeyValue = Object::get_property(ctx, object, encryption_key_string); - if (!Value::is_undefined(ctx, encryptionKeyValue)) { - std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue); - config.encryption_key = std::vector(encryptionKey.begin(), encryptionKey.end()); + TValue encryption_key_value = Object::get_property(ctx, object, encryption_key_string); + if (!Value::is_undefined(ctx, encryption_key_value)) { + std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value); + config.encryption_key = std::vector(encryption_key.begin(), encryption_key.end()); } } } @@ -371,9 +371,9 @@ template void Realm::create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2, 3); - SharedRealm sharedRealm = *get_internal>(this_object); - std::string className = validated_object_type_for_value(sharedRealm, ctx, arguments[0]); - auto &schema = sharedRealm->config().schema; + SharedRealm realm = *get_internal>(this_object); + std::string className = validated_object_type_for_value(realm, ctx, arguments[0]); + auto &schema = realm->config().schema; auto object_schema = schema->find(className); if (object_schema == schema->end()) { @@ -390,7 +390,7 @@ void Realm::create(TContext ctx, TObject this_object, size_t argc, const TVal update = Value::validated_to_boolean(ctx, arguments[2], "update"); } - auto realm_object = realm::Object::create(ctx, sharedRealm, *object_schema, object, update); + auto realm_object = realm::Object::create(ctx, realm, *object_schema, object, update); return_value.set(RealmObject::create_instance(ctx, realm_object)); } From 1ca5a4398263e4a6a2bc563c2106a71fbcea3f43 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 12:15:00 -0700 Subject: [PATCH 33/48] Revert TValue to ValueType, etc. --- src/js_list.hpp | 56 +++++------ src/js_object_accessor.hpp | 72 +++++++------- src/js_realm.hpp | 140 +++++++++++++------------- src/js_realm_object.hpp | 32 +++--- src/js_results.hpp | 60 ++++++------ src/js_schema.hpp | 56 +++++------ src/js_types.hpp | 194 ++++++++++++++++++------------------- 7 files changed, 305 insertions(+), 305 deletions(-) diff --git a/src/js_list.hpp b/src/js_list.hpp index 04959f04..63207841 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -34,30 +34,30 @@ namespace js { template class List { - using TContext = typename T::Context; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; public: - static TObject create_instance(TContext, realm::List &); + static ObjectType create_instance(ContextType, realm::List &); // properties - static void get_length(TContext, TObject, ReturnValue &); - static void get_index(TContext, TObject, uint32_t, ReturnValue &); - static bool set_index(TContext, TObject, uint32_t, TValue); + static void get_length(ContextType, ObjectType, ReturnValue &); + static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &); + static bool set_index(ContextType, ObjectType, uint32_t, ValueType); // methods - static void push(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void pop(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void unshift(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void shift(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void splice(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void snapshot(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void push(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void pop(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void unshift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void shift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void splice(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); }; template @@ -85,18 +85,18 @@ struct ListClass : ClassDefinition> { }; template -typename T::Object List::create_instance(TContext ctx, realm::List &list) { +typename T::Object List::create_instance(ContextType ctx, realm::List &list) { return create_object>(ctx, new realm::List(list)); } template -void List::get_length(TContext ctx, TObject object, ReturnValue &return_value) { +void List::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } template -void List::get_index(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { +void List::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { auto list = get_internal>(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); @@ -104,14 +104,14 @@ void List::get_index(TContext ctx, TObject object, uint32_t index, ReturnValu } template -bool List::set_index(TContext ctx, TObject object, uint32_t index, TValue value) { +bool List::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { auto list = get_internal>(object); list->set(ctx, value, index); return true; } template -void List::push(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::push(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -123,7 +123,7 @@ void List::push(TContext ctx, TObject this_object, size_t argc, const TValue } template -void List::pop(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::pop(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -142,7 +142,7 @@ void List::pop(TContext ctx, TObject this_object, size_t argc, const TValue a } template -void List::unshift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::unshift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -154,7 +154,7 @@ void List::unshift(TContext ctx, TObject this_object, size_t argc, const TVal } template -void List::shift(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::shift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -171,7 +171,7 @@ void List::shift(TContext ctx, TObject this_object, size_t argc, const TValue } template -void List::splice(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::splice(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -190,7 +190,7 @@ void List::splice(TContext ctx, TObject this_object, size_t argc, const TValu remove = std::min(remove, size - index); } - std::vector removed_objects; + std::vector removed_objects; removed_objects.reserve(remove); for (size_t i = 0; i < remove; i++) { @@ -207,7 +207,7 @@ void List::splice(TContext ctx, TObject this_object, size_t argc, const TValu } template -void List::snapshot(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); @@ -215,7 +215,7 @@ void List::snapshot(TContext ctx, TObject this_object, size_t argc, const TVa } template -void List::filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); @@ -223,7 +223,7 @@ void List::filtered(TContext ctx, TObject this_object, size_t argc, const TVa } template -void List::sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void List::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto list = get_internal>(this_object); diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index 89c332d4..dd513247 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -27,81 +27,81 @@ namespace js { template struct NativeAccessor { - using TContext = typename T::Context; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using Object = Object; using Value = Value; - static bool dict_has_value_for_key(TContext ctx, TValue dict, const std::string &prop_name) { - TObject object = Value::validated_to_object(ctx, dict); + static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { + ObjectType object = Value::validated_to_object(ctx, dict); return Object::has_property(ctx, object, prop_name); } - static TValue dict_value_for_key(TContext ctx, TValue dict, const std::string &prop_name) { - TObject object = Value::validated_to_object(ctx, dict); + static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { + ObjectType object = Value::validated_to_object(ctx, dict); return Object::get_property(ctx, object, prop_name); } - static bool has_default_value_for_property(TContext ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + static bool has_default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; return defaults.count(prop_name) != 0; } - static TValue default_value_for_property(TContext ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { + static ValueType default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) { auto defaults = get_delegate(realm)->m_defaults[object_schema.name]; return defaults.at(prop_name); } // These must be implemented for each JS engine. - static std::string to_binary(TContext, TValue &); - static TValue from_binary(TContext, BinaryData); + static std::string to_binary(ContextType, ValueType &); + static ValueType from_binary(ContextType, BinaryData); - static bool to_bool(TContext ctx, TValue &value) { + static bool to_bool(ContextType ctx, ValueType &value) { return Value::validated_to_boolean(ctx, value, "Property"); } - static TValue from_bool(TContext ctx, bool boolean) { + static ValueType from_bool(ContextType ctx, bool boolean) { return Value::from_boolean(ctx, boolean); } - static long long to_long(TContext ctx, TValue &value) { + static long long to_long(ContextType ctx, ValueType &value) { return Value::validated_to_number(ctx, value, "Property"); } - static TValue from_long(TContext ctx, long long number) { + static ValueType from_long(ContextType ctx, long long number) { return Value::from_number(ctx, number); } - static float to_float(TContext ctx, TValue &value) { + static float to_float(ContextType ctx, ValueType &value) { return Value::validated_to_number(ctx, value, "Property"); } - static TValue from_float(TContext ctx, float number) { + static ValueType from_float(ContextType ctx, float number) { return Value::from_number(ctx, number); } - static double to_double(TContext ctx, TValue &value) { + static double to_double(ContextType ctx, ValueType &value) { return Value::validated_to_number(ctx, value, "Property"); } - static TValue from_double(TContext ctx, double number) { + static ValueType from_double(ContextType ctx, double number) { return Value::from_number(ctx, number); } - static std::string to_string(TContext ctx, TValue &value) { + static std::string to_string(ContextType ctx, ValueType &value) { return Value::validated_to_string(ctx, value, "Property"); } - static TValue from_string(TContext ctx, StringData string) { + static ValueType from_string(ContextType ctx, StringData string) { return Value::from_string(ctx, string.data()); } - static DateTime to_datetime(TContext ctx, TValue &value) { - TObject date = Value::validated_to_date(ctx, value, "Property"); + static DateTime to_datetime(ContextType ctx, ValueType &value) { + ObjectType date = Value::validated_to_date(ctx, value, "Property"); return DateTime(Value::to_number(ctx, date)); } - static TValue from_datetime(TContext ctx, DateTime dt) { + static ValueType from_datetime(ContextType ctx, DateTime dt) { return Object::create_date(ctx, dt.get_datetime()); } - static bool is_null(TContext ctx, TValue &value) { + static bool is_null(ContextType ctx, ValueType &value) { return Value::is_null(ctx, value) || Value::is_undefined(ctx, value); } - static TValue null_value(TContext ctx) { + static ValueType null_value(ContextType ctx) { return Value::from_null(ctx); } - static size_t to_object_index(TContext ctx, SharedRealm realm, TValue &value, const std::string &type, bool try_update) { - TObject object = Value::validated_to_object(ctx, value); + static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &value, const std::string &type, bool try_update) { + ObjectType object = Value::validated_to_object(ctx, value); if (Object::template is_instance>(ctx, object)) { return get_internal>(object)->row().get_index(); } @@ -111,31 +111,31 @@ struct NativeAccessor { object = Schema::dict_for_property_array(ctx, *object_schema, object); } - auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); + auto child = realm::Object::create(ctx, realm, *object_schema, static_cast(object), try_update); return child.row().get_index(); } - static size_t to_existing_object_index(TContext ctx, TValue &value) { - TObject object = Value::validated_to_object(ctx, value); + static size_t to_existing_object_index(ContextType ctx, ValueType &value) { + ObjectType object = Value::validated_to_object(ctx, value); if (Object::template is_instance>(ctx, object)) { return get_internal>(object)->row().get_index(); } throw std::runtime_error("object is not a Realm Object"); } - static TValue from_object(TContext ctx, realm::Object realm_object) { + static ValueType from_object(ContextType ctx, realm::Object realm_object) { return RealmObject::create_instance(ctx, realm_object); } - static size_t list_size(TContext ctx, TValue &value) { + static size_t list_size(ContextType ctx, ValueType &value) { return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value)); } - static TValue list_value_at_index(TContext ctx, TValue &value, size_t index) { + static ValueType list_value_at_index(ContextType ctx, ValueType &value, size_t index) { return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index); } - static TValue from_list(TContext ctx, realm::List list) { + static ValueType from_list(ContextType ctx, realm::List list) { return List::create_instance(ctx, list); } - static Mixed to_mixed(TContext ctx, TValue &val) { + static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); } }; diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 83d713b7..c3c21d32 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -43,10 +43,10 @@ struct RealmClass; template class RealmDelegate : public BindingContext { public: - using TGlobalContext = typename T::GlobalContext; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using GlobalContextType = typename T::GlobalContext; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using Value = Value; using ObjectDefaultsMap = typename Schema::ObjectDefaultsMap; @@ -60,13 +60,13 @@ class RealmDelegate : public BindingContext { } virtual void will_change(std::vector const& observers, std::vector const& invalidated) {} - RealmDelegate(std::weak_ptr realm, TGlobalContext ctx) : m_context(ctx), m_realm(realm) {} + RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {} ~RealmDelegate() { remove_all_notifications(); } - void add_notification(TFunction notification) { + void add_notification(FunctionType notification) { for (auto &handler : m_notifications) { if (handler == notification) { return; @@ -74,7 +74,7 @@ class RealmDelegate : public BindingContext { } m_notifications.emplace_back(m_context, notification); } - void remove_notification(TFunction notification) { + void remove_notification(FunctionType notification) { for (auto iter = m_notifications.begin(); iter != m_notifications.end(); ++iter) { if (*iter == notification) { m_notifications.erase(iter); @@ -90,8 +90,8 @@ class RealmDelegate : public BindingContext { ConstructorMap m_constructors; private: - Protected m_context; - std::list> m_notifications; + Protected m_context; + std::list> m_notifications; std::weak_ptr m_realm; void notify(const char *notification_name) { @@ -100,8 +100,8 @@ class RealmDelegate : public BindingContext { throw std::runtime_error("Realm no longer exists"); } - TObject realm_object = create_object>(m_context, new SharedRealm(realm)); - TValue arguments[2]; + ObjectType realm_object = create_object>(m_context, new SharedRealm(realm)); + ValueType arguments[2]; arguments[0] = realm_object; arguments[1] = Value::from_string(m_context, notification_name); @@ -117,45 +117,45 @@ void delete_all_realms(); template class Realm { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using String = String; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; - using NativeAccessor = realm::NativeAccessor; + using NativeAccessor = realm::NativeAccessor; public: - static TFunction create_constructor(TContext); + static FunctionType create_constructor(ContextType); // methods - static void objects(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void create(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void delete_one(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void delete_all(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void write(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void add_listener(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void remove_listener(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void remove_all_listeners(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void close(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void delete_one(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void delete_all(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void write(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void close(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); // properties - static void get_path(TContext, TObject, ReturnValue &); - static void get_schema_version(TContext, TObject, ReturnValue &); + static void get_path(ContextType, ObjectType, ReturnValue &); + static void get_schema_version(ContextType, ObjectType, ReturnValue &); // static methods - static void constructor(TContext, TObject, size_t, const TValue[]); - static void schema_version(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void clear_test_state(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void constructor(ContextType, ObjectType, size_t, const ValueType[]); + static void schema_version(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void clear_test_state(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); // static properties - static void get_default_path(TContext, TObject, ReturnValue &); - static void set_default_path(TContext, TObject, TValue value); + static void get_default_path(ContextType, ObjectType, ReturnValue &); + static void set_default_path(ContextType, ObjectType, ValueType value); private: - static std::string validated_notification_name(TContext ctx, const TValue &value) { + static std::string validated_notification_name(ContextType ctx, const ValueType &value) { std::string name = Value::validated_to_string(ctx, value, "notification name"); if (name != "change") { throw std::runtime_error("Only the 'change' notification name is supported."); @@ -164,13 +164,13 @@ class Realm { } // converts constructor object or type name to type name - static std::string validated_object_type_for_value(SharedRealm &realm, TContext ctx, const TValue &value) { + static std::string validated_object_type_for_value(SharedRealm &realm, ContextType ctx, const ValueType &value) { if (Value::is_constructor(ctx, value)) { - TFunction constructor = Value::to_constructor(ctx, value); + FunctionType constructor = Value::to_constructor(ctx, value); auto delegate = get_delegate(realm.get()); for (auto &pair : delegate->m_constructors) { - if (TFunction(pair.second) == constructor) { + if (FunctionType(pair.second) == constructor) { return pair.first; } } @@ -223,11 +223,11 @@ struct RealmClass : ClassDefinition { }; template -inline typename T::Function Realm::create_constructor(TContext ctx) { - TFunction realm_constructor = ObjectWrap>::create_constructor(ctx); - TFunction collection_constructor = ObjectWrap>::create_constructor(ctx); - TFunction list_constructor = ObjectWrap>::create_constructor(ctx); - TFunction results_constructor = ObjectWrap>::create_constructor(ctx); +inline typename T::Function Realm::create_constructor(ContextType ctx) { + FunctionType realm_constructor = ObjectWrap>::create_constructor(ctx); + FunctionType collection_constructor = ObjectWrap>::create_constructor(ctx); + FunctionType list_constructor = ObjectWrap>::create_constructor(ctx); + FunctionType results_constructor = ObjectWrap>::create_constructor(ctx); PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete); Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes); @@ -238,7 +238,7 @@ inline typename T::Function Realm::create_constructor(TContext ctx) { } template -void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const TValue arguments[]) { +void Realm::constructor(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[]) { static const String path_string = "path"; static const String schema_string = "schema"; static const String schema_version_string = "schemaVersion"; @@ -252,14 +252,14 @@ void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const config.path = default_path(); } else if (argc == 1) { - TValue value = arguments[0]; + ValueType value = arguments[0]; if (Value::is_string(ctx, value)) { config.path = Value::validated_to_string(ctx, value, "path"); } else if (Value::is_object(ctx, value)) { - TObject object = Value::validated_to_object(ctx, value); + ObjectType object = Value::validated_to_object(ctx, value); - TValue path_value = Object::get_property(ctx, object, path_string); + ValueType path_value = Object::get_property(ctx, object, path_string); if (!Value::is_undefined(ctx, path_value)) { config.path = Value::validated_to_string(ctx, path_value, "path"); } @@ -267,13 +267,13 @@ void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const config.path = js::default_path(); } - TValue schema_value = Object::get_property(ctx, object, schema_string); + ValueType schema_value = Object::get_property(ctx, object, schema_string); if (!Value::is_undefined(ctx, schema_value)) { - TObject schema_object = Value::validated_to_object(ctx, schema_value, "schema"); + ObjectType schema_object = Value::validated_to_object(ctx, schema_value, "schema"); config.schema.reset(new realm::Schema(Schema::parse_schema(ctx, schema_object, defaults, constructors))); } - TValue version_value = Object::get_property(ctx, object, schema_version_string); + ValueType version_value = Object::get_property(ctx, object, schema_version_string); if (!Value::is_undefined(ctx, version_value)) { config.schema_version = Value::validated_to_number(ctx, version_value, "schemaVersion"); } @@ -281,7 +281,7 @@ void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const config.schema_version = 0; } - TValue encryption_key_value = Object::get_property(ctx, object, encryption_key_string); + ValueType encryption_key_value = Object::get_property(ctx, object, encryption_key_string); if (!Value::is_undefined(ctx, encryption_key_value)) { std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value); config.encryption_key = std::vector(encryption_key.begin(), encryption_key.end()); @@ -309,7 +309,7 @@ void Realm::constructor(TContext ctx, TObject this_object, size_t argc, const } template -void Realm::schema_version(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::schema_version(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); realm::Realm::Config config; @@ -330,35 +330,35 @@ void Realm::schema_version(TContext ctx, TObject this_object, size_t argc, co } template -void Realm::clear_test_state(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); delete_all_realms(); } template -void Realm::get_default_path(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); } template -void Realm::set_default_path(TContext ctx, TObject object, TValue value) { +void Realm::set_default_path(ContextType ctx, ObjectType object, ValueType value) { js::set_default_path(Value::validated_to_string(ctx, value, "defaultPath")); } template -void Realm::get_path(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_path(ContextType ctx, ObjectType object, ReturnValue &return_value) { std::string path = get_internal>(object)->get()->config().path; return_value.set(path); } template -void Realm::get_schema_version(TContext ctx, TObject object, ReturnValue &return_value) { +void Realm::get_schema_version(ContextType ctx, ObjectType object, ReturnValue &return_value) { double version = get_internal>(object)->get()->config().schema_version; return_value.set(version); } template -void Realm::objects(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); @@ -368,7 +368,7 @@ void Realm::objects(TContext ctx, TObject this_object, size_t argc, const TVa } template -void Realm::create(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::create(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2, 3); SharedRealm realm = *get_internal>(this_object); @@ -380,7 +380,7 @@ void Realm::create(TContext ctx, TObject this_object, size_t argc, const TVal throw std::runtime_error("Object type '" + className + "' not found in schema."); } - TObject object = Value::validated_to_object(ctx, arguments[1], "properties"); + ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties"); if (Value::is_array(ctx, arguments[1])) { object = Schema::dict_for_property_array(ctx, *object_schema, object); } @@ -390,12 +390,12 @@ void Realm::create(TContext ctx, TObject this_object, size_t argc, const TVal update = Value::validated_to_boolean(ctx, arguments[2], "update"); } - auto realm_object = realm::Object::create(ctx, realm, *object_schema, object, update); + auto realm_object = realm::Object::create(ctx, realm, *object_schema, object, update); return_value.set(RealmObject::create_instance(ctx, realm_object)); } template -void Realm::delete_one(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::delete_one(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); @@ -403,7 +403,7 @@ void Realm::delete_one(TContext ctx, TObject this_object, size_t argc, const throw std::runtime_error("Can only delete objects within a transaction."); } - TObject arg = Value::validated_to_object(ctx, arguments[0]); + ObjectType arg = Value::validated_to_object(ctx, arguments[0]); if (Object::template is_instance>(ctx, arg)) { auto object = get_internal>(arg); @@ -413,7 +413,7 @@ void Realm::delete_one(TContext ctx, TObject this_object, size_t argc, const else if (Value::is_array(ctx, arg)) { uint32_t length = Object::validated_get_length(ctx, arg); for (uint32_t i = length; i--;) { - TObject object = Object::validated_get_object(ctx, arg, i); + ObjectType object = Object::validated_get_object(ctx, arg, i); if (!Object::template is_instance>(ctx, object)) { throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects."); @@ -438,7 +438,7 @@ void Realm::delete_one(TContext ctx, TObject this_object, size_t argc, const } template -void Realm::delete_all(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::delete_all(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal>(this_object); @@ -453,11 +453,11 @@ void Realm::delete_all(TContext ctx, TObject this_object, size_t argc, const } template -void Realm::write(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::write(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); SharedRealm realm = *get_internal>(this_object); - TFunction callback = Value::validated_to_function(ctx, arguments[0]); + FunctionType callback = Value::validated_to_function(ctx, arguments[0]); try { realm->begin_transaction(); @@ -473,7 +473,7 @@ void Realm::write(TContext ctx, TObject this_object, size_t argc, const TValu } template -void Realm::add_listener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -484,7 +484,7 @@ void Realm::add_listener(TContext ctx, TObject this_object, size_t argc, cons } template -void Realm::remove_listener(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); __unused std::string name = validated_notification_name(ctx, arguments[0]); @@ -495,7 +495,7 @@ void Realm::remove_listener(TContext ctx, TObject this_object, size_t argc, c } template -void Realm::remove_all_listeners(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0, 1); if (argc) { validated_notification_name(ctx, arguments[0]); @@ -506,7 +506,7 @@ void Realm::remove_all_listeners(TContext ctx, TObject this_object, size_t ar } template -void Realm::close(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Realm::close(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); SharedRealm realm = *get_internal>(this_object); diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index b3629747..48b4a97d 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -30,10 +30,10 @@ namespace js { template class RealmObject { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using String = String; using Value = Value; using Object = Object; @@ -41,11 +41,11 @@ class RealmObject { using ReturnValue = ReturnValue; public: - static TObject create_instance(TContext, realm::Object &); + static ObjectType create_instance(ContextType, realm::Object &); - static void get_property(TContext, TObject, const String &, ReturnValue &); - static bool set_property(TContext, TObject, const String &, TValue); - static std::vector get_property_names(TContext, TObject); + static void get_property(ContextType, ObjectType, const String &, ReturnValue &); + static bool set_property(ContextType, ObjectType, const String &, ValueType); + static std::vector get_property_names(ContextType, ObjectType); }; template @@ -62,7 +62,7 @@ struct RealmObjectClass : ClassDefinition { }; template -typename T::Object RealmObject::create_instance(TContext ctx, realm::Object &realm_object) { +typename T::Object RealmObject::create_instance(ContextType ctx, realm::Object &realm_object) { static String prototype_string = "prototype"; auto delegate = get_delegate(realm_object.realm().get()); @@ -73,11 +73,11 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & return object; } - TFunction constructor = delegate->m_constructors.at(name); - TObject prototype = Object::validated_get_object(ctx, constructor, prototype_string); + FunctionType constructor = delegate->m_constructors.at(name); + ObjectType prototype = Object::validated_get_object(ctx, constructor, prototype_string); Object::set_prototype(ctx, object, prototype); - TValue result = Function::call(ctx, constructor, object, 0, NULL); + ValueType result = Function::call(ctx, constructor, object, 0, NULL); if (result != object && !Value::is_null(ctx, result) && !Value::is_undefined(ctx, result)) { throw std::runtime_error("Realm object constructor must not return another value"); } @@ -86,10 +86,10 @@ typename T::Object RealmObject::create_instance(TContext ctx, realm::Object & } template -void RealmObject::get_property(TContext ctx, TObject object, const String &property, ReturnValue &return_value) { +void RealmObject::get_property(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { try { auto realm_object = get_internal>(object); - auto result = realm_object->template get_property_value(ctx, property); + auto result = realm_object->template get_property_value(ctx, property); return_value.set(result); } catch (InvalidPropertyException &ex) { // getters for nonexistent properties in JS should always return undefined @@ -97,14 +97,14 @@ void RealmObject::get_property(TContext ctx, TObject object, const String &pr } template -bool RealmObject::set_property(TContext ctx, TObject object, const String &property, TValue value) { +bool RealmObject::set_property(ContextType ctx, ObjectType object, const String &property, ValueType value) { auto realm_object = get_internal>(object); realm_object->set_property_value(ctx, property, value, true); return true; } template -std::vector> RealmObject::get_property_names(TContext ctx, TObject object) { +std::vector> RealmObject::get_property_names(ContextType ctx, ObjectType object) { auto realm_object = get_internal>(object); auto &properties = realm_object->get_object_schema().properties; diff --git a/src/js_results.hpp b/src/js_results.hpp index fb6a516a..92b72c45 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -31,31 +31,31 @@ namespace js { template class Results { - using TContext = typename T::Context; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using Object = Object; using Value = Value; using ReturnValue = ReturnValue; public: - static TObject create_instance(TContext, const realm::Results &, bool live = true); - static TObject create_instance(TContext, const realm::List &, bool live = true); - static TObject create_instance(TContext, SharedRealm, const std::string &type, bool live = true); - static TObject create_instance(TContext, SharedRealm, const ObjectSchema &, Query, bool live = true); + static ObjectType create_instance(ContextType, const realm::Results &, bool live = true); + static ObjectType create_instance(ContextType, const realm::List &, bool live = true); + static ObjectType create_instance(ContextType, SharedRealm, const std::string &type, bool live = true); + static ObjectType create_instance(ContextType, SharedRealm, const ObjectSchema &, Query, bool live = true); template - static TObject create_filtered(TContext, const U &, size_t, const TValue[]); + static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]); template - static TObject create_sorted(TContext, const U &, size_t, const TValue[]); + static ObjectType create_sorted(ContextType, const U &, size_t, const ValueType[]); - static void get_length(TContext, TObject, ReturnValue &); - static void get_index(TContext, TObject, uint32_t, ReturnValue &); + static void get_length(ContextType, ObjectType, ReturnValue &); + static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &); - static void snapshot(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void filtered(TContext, TObject, size_t, const TValue[], ReturnValue &); - static void sorted(TContext, TObject, size_t, const TValue[], ReturnValue &); + static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &); }; template @@ -78,7 +78,7 @@ struct ResultsClass : ClassDefinition> { }; template -typename T::Object Results::create_instance(TContext ctx, const realm::Results &results, bool live) { +typename T::Object Results::create_instance(ContextType ctx, const realm::Results &results, bool live) { auto new_results = new realm::Results(results); new_results->set_live(live); @@ -86,12 +86,12 @@ typename T::Object Results::create_instance(TContext ctx, const realm::Result } template -typename T::Object Results::create_instance(TContext ctx, const realm::List &list, bool live) { +typename T::Object Results::create_instance(ContextType ctx, const realm::List &list, bool live) { return create_instance(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live); } template -typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, const std::string &type, bool live) { +typename T::Object Results::create_instance(ContextType ctx, SharedRealm realm, const std::string &type, bool live) { auto table = ObjectStore::table_for_object_type(realm->read_group(), type); auto &schema = realm->config().schema; auto object_schema = schema->find(type); @@ -107,7 +107,7 @@ typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, } template -typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { +typename T::Object Results::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) { auto results = new realm::Results(realm, object_schema, std::move(query)); results->set_live(live); @@ -116,13 +116,13 @@ typename T::Object Results::create_instance(TContext ctx, SharedRealm realm, template template -typename T::Object Results::create_filtered(TContext ctx, const U &collection, size_t argc, const TValue arguments[]) { +typename T::Object Results::create_filtered(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { auto query_string = Value::validated_to_string(ctx, arguments[0], "predicate"); auto query = collection.get_query(); auto const &realm = collection.get_realm(); auto const &object_schema = collection.get_object_schema(); - std::vector args; + std::vector args; args.reserve(argc - 1); for (size_t i = 1; i < argc; i++) { @@ -130,7 +130,7 @@ typename T::Object Results::create_filtered(TContext ctx, const U &collection } parser::Predicate predicate = parser::parse(query_string); - query_builder::ArgumentConverter converter(ctx, args); + query_builder::ArgumentConverter converter(ctx, args); query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name); return create_instance(ctx, realm, object_schema, std::move(query)); @@ -138,7 +138,7 @@ typename T::Object Results::create_filtered(TContext ctx, const U &collection template template -typename T::Object Results::create_sorted(TContext ctx, const U &collection, size_t argc, const TValue arguments[]) { +typename T::Object Results::create_sorted(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) { auto const &realm = collection.get_realm(); auto const &object_schema = collection.get_object_schema(); std::vector prop_names; @@ -148,7 +148,7 @@ typename T::Object Results::create_sorted(TContext ctx, const U &collection, if (Value::is_array(ctx, arguments[0])) { validate_argument_count(argc, 1, "Second argument is not allowed if passed an array of sort descriptors"); - TObject js_prop_names = Value::validated_to_object(ctx, arguments[0]); + ObjectType js_prop_names = Value::validated_to_object(ctx, arguments[0]); prop_count = Object::validated_get_length(ctx, js_prop_names); if (!prop_count) { throw std::invalid_argument("Sort descriptor array must not be empty"); @@ -158,10 +158,10 @@ typename T::Object Results::create_sorted(TContext ctx, const U &collection, ascending.resize(prop_count); for (unsigned int i = 0; i < prop_count; i++) { - TValue value = Object::validated_get_property(ctx, js_prop_names, i); + ValueType value = Object::validated_get_property(ctx, js_prop_names, i); if (Value::is_array(ctx, value)) { - TObject array = Value::to_array(ctx, value); + ObjectType array = Value::to_array(ctx, value); prop_names[i] = Object::validated_get_string(ctx, array, 0); ascending[i] = !Object::validated_get_boolean(ctx, array, 1); } @@ -195,13 +195,13 @@ typename T::Object Results::create_sorted(TContext ctx, const U &collection, } template -void Results::get_length(TContext ctx, TObject object, ReturnValue &return_value) { +void Results::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) { auto results = get_internal>(object); return_value.set((uint32_t)results->size()); } template -void Results::get_index(TContext ctx, TObject object, uint32_t index, ReturnValue &return_value) { +void Results::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { auto results = get_internal>(object); auto row = results->get(index); @@ -216,7 +216,7 @@ void Results::get_index(TContext ctx, TObject object, uint32_t index, ReturnV } template -void Results::snapshot(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto results = get_internal>(this_object); @@ -224,7 +224,7 @@ void Results::snapshot(TContext ctx, TObject this_object, size_t argc, const } template -void Results::filtered(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto results = get_internal>(this_object); @@ -232,7 +232,7 @@ void Results::filtered(TContext ctx, TObject this_object, size_t argc, const } template -void Results::sorted(TContext ctx, TObject this_object, size_t argc, const TValue arguments[], ReturnValue &return_value) { +void Results::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto results = get_internal>(this_object); diff --git a/src/js_schema.hpp b/src/js_schema.hpp index 75fc310f..31927abd 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -28,36 +28,36 @@ namespace js { template struct Schema { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; using String = String; using Object = Object; using Value = Value; - using ObjectDefaults = std::map>; + using ObjectDefaults = std::map>; using ObjectDefaultsMap = std::map; - using ConstructorMap = std::map>; + using ConstructorMap = std::map>; - static TObject dict_for_property_array(TContext, const ObjectSchema &, TObject); - static Property parse_property(TContext, TValue, std::string, ObjectDefaults &); - static ObjectSchema parse_object_schema(TContext, TObject, ObjectDefaultsMap &, ConstructorMap &); - static realm::Schema parse_schema(TContext, TObject, ObjectDefaultsMap &, ConstructorMap &); + static ObjectType dict_for_property_array(ContextType, const ObjectSchema &, ObjectType); + static Property parse_property(ContextType, ValueType, std::string, ObjectDefaults &); + static ObjectSchema parse_object_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); + static realm::Schema parse_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &); }; template -typename T::Object Schema::dict_for_property_array(TContext ctx, const ObjectSchema &object_schema, TObject array) { +typename T::Object Schema::dict_for_property_array(ContextType ctx, const ObjectSchema &object_schema, ObjectType array) { size_t count = object_schema.properties.size(); if (count != Object::validated_get_length(ctx, array)) { throw std::runtime_error("Array must contain values for all object properties"); } - TObject dict = Object::create_empty(ctx); + ObjectType dict = Object::create_empty(ctx); for (uint32_t i = 0; i < count; i++) { - TValue value = Object::get_property(ctx, array, i); + ValueType value = Object::get_property(ctx, array, i); Object::set_property(ctx, dict, object_schema.properties[i].name, value); } @@ -65,7 +65,7 @@ typename T::Object Schema::dict_for_property_array(TContext ctx, const Object } template -Property Schema::parse_property(TContext ctx, TValue attributes, std::string property_name, ObjectDefaults &object_defaults) { +Property Schema::parse_property(ContextType ctx, ValueType attributes, std::string property_name, ObjectDefaults &object_defaults) { static const String default_string = "default"; static const String indexed_string = "indexed"; static const String type_string = "type"; @@ -75,14 +75,14 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string Property prop; prop.name = property_name; - TObject property_object = {}; + ObjectType property_object = {}; std::string type; if (Value::is_object(ctx, attributes)) { property_object = Value::validated_to_object(ctx, attributes); type = Object::validated_get_string(ctx, property_object, type_string); - TValue optional_value = Object::get_property(ctx, property_object, optional_string); + ValueType optional_value = Object::get_property(ctx, property_object, optional_string); if (!Value::is_undefined(ctx, optional_value)) { prop.is_nullable = Value::validated_to_boolean(ctx, optional_value, "optional"); } @@ -136,12 +136,12 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string } if (Value::is_valid(property_object)) { - TValue default_value = Object::get_property(ctx, property_object, default_string); + ValueType default_value = Object::get_property(ctx, property_object, default_string); if (!Value::is_undefined(ctx, default_value)) { - object_defaults.emplace(prop.name, Protected(ctx, default_value)); + object_defaults.emplace(prop.name, Protected(ctx, default_value)); } - TValue indexed_value = Object::get_property(ctx, property_object, indexed_string); + ValueType indexed_value = Object::get_property(ctx, property_object, indexed_string); if (!Value::is_undefined(ctx, indexed_value)) { prop.is_indexed = Value::validated_to_boolean(ctx, indexed_value); } @@ -151,13 +151,13 @@ Property Schema::parse_property(TContext ctx, TValue attributes, std::string } template -ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { +ObjectSchema Schema::parse_object_schema(ContextType ctx, ObjectType object_schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { static const String name_string = "name"; static const String primary_string = "primaryKey"; static const String properties_string = "properties"; static const String schema_string = "schema"; - TFunction object_constructor = {}; + FunctionType object_constructor = {}; if (Value::is_constructor(ctx, object_schema_object)) { object_constructor = Value::to_constructor(ctx, object_schema_object); object_schema_object = Object::validated_get_object(ctx, object_constructor, schema_string, "Realm object constructor must have a 'schema' property."); @@ -167,11 +167,11 @@ ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_ ObjectSchema object_schema; object_schema.name = Object::validated_get_string(ctx, object_schema_object, name_string); - TObject properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema must have a 'properties' object."); + ObjectType properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema must have a 'properties' object."); if (Value::is_array(ctx, properties_object)) { uint32_t length = Object::validated_get_length(ctx, properties_object); for (uint32_t i = 0; i < length; i++) { - TObject property_object = Object::validated_get_object(ctx, properties_object, i); + ObjectType property_object = Object::validated_get_object(ctx, properties_object, i); std::string property_name = Object::validated_get_string(ctx, property_object, name_string); object_schema.properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults)); } @@ -179,12 +179,12 @@ ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_ else { auto property_names = Object::get_property_names(ctx, properties_object); for (auto &property_name : property_names) { - TValue property_value = Object::get_property(ctx, properties_object, property_name); + ValueType property_value = Object::get_property(ctx, properties_object, property_name); object_schema.properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults)); } } - TValue primary_value = Object::get_property(ctx, object_schema_object, primary_string); + ValueType primary_value = Object::get_property(ctx, object_schema_object, primary_string); if (!Value::is_undefined(ctx, primary_value)) { object_schema.primary_key = Value::validated_to_string(ctx, primary_value); Property *property = object_schema.primary_key_property(); @@ -196,7 +196,7 @@ ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_ // Store prototype so that objects of this type will have their prototype set to this prototype object. if (Value::is_valid(object_constructor)) { - constructors.emplace(object_schema.name, Protected(ctx, object_constructor)); + constructors.emplace(object_schema.name, Protected(ctx, object_constructor)); } defaults.emplace(object_schema.name, std::move(object_defaults)); @@ -205,12 +205,12 @@ ObjectSchema Schema::parse_object_schema(TContext ctx, TObject object_schema_ } template -realm::Schema Schema::parse_schema(TContext ctx, TObject schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { +realm::Schema Schema::parse_schema(ContextType ctx, ObjectType schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) { std::vector schema; uint32_t length = Object::validated_get_length(ctx, schema_object); for (uint32_t i = 0; i < length; i++) { - TObject object_schema_object = Object::validated_get_object(ctx, schema_object, i); + ObjectType object_schema_object = Object::validated_get_object(ctx, schema_object, i); ObjectSchema object_schema = parse_object_schema(ctx, object_schema_object, defaults, constructors); schema.emplace_back(std::move(object_schema)); } diff --git a/src/js_types.hpp b/src/js_types.hpp index 3bd81bf2..77e0db39 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -58,50 +58,50 @@ struct String { template struct Context { - using TContext = typename T::Context; - using GlobalTContext = typename T::GlobalContext; + using ContextType = typename T::Context; + using GlobalContextType = typename T::GlobalContext; - static GlobalTContext get_global_context(TContext); + static GlobalContextType get_global_context(ContextType); }; template struct Value { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; - static bool is_array(TContext, const TValue &); - static bool is_array_buffer(TContext, const TValue &); - static bool is_array_buffer_view(TContext, const TValue &); - static bool is_boolean(TContext, const TValue &); - static bool is_constructor(TContext, const TValue &); - static bool is_date(TContext, const TValue &); - static bool is_function(TContext, const TValue &); - static bool is_null(TContext, const TValue &); - static bool is_number(TContext, const TValue &); - static bool is_object(TContext, const TValue &); - static bool is_string(TContext, const TValue &); - static bool is_undefined(TContext, const TValue &); - static bool is_valid(const TValue &); + static bool is_array(ContextType, const ValueType &); + static bool is_array_buffer(ContextType, const ValueType &); + static bool is_array_buffer_view(ContextType, const ValueType &); + static bool is_boolean(ContextType, const ValueType &); + static bool is_constructor(ContextType, const ValueType &); + static bool is_date(ContextType, const ValueType &); + static bool is_function(ContextType, const ValueType &); + static bool is_null(ContextType, const ValueType &); + static bool is_number(ContextType, const ValueType &); + static bool is_object(ContextType, const ValueType &); + static bool is_string(ContextType, const ValueType &); + static bool is_undefined(ContextType, const ValueType &); + static bool is_valid(const ValueType &); - static TValue from_boolean(TContext, bool); - static TValue from_null(TContext); - static TValue from_number(TContext, double); - static TValue from_string(TContext, const String &); - static TValue from_undefined(TContext); + static ValueType from_boolean(ContextType, bool); + static ValueType from_null(ContextType); + static ValueType from_number(ContextType, double); + static ValueType from_string(ContextType, const String &); + static ValueType from_undefined(ContextType); - static TObject to_array(TContext, const TValue &); - static bool to_boolean(TContext, const TValue &); - static TFunction to_constructor(TContext, const TValue &); - static TObject to_date(TContext, const TValue &); - static TFunction to_function(TContext, const TValue &); - static double to_number(TContext, const TValue &); - static TObject to_object(TContext, const TValue &); - static String to_string(TContext, const TValue &); + static ObjectType to_array(ContextType, const ValueType &); + static bool to_boolean(ContextType, const ValueType &); + static FunctionType to_constructor(ContextType, const ValueType &); + static ObjectType to_date(ContextType, const ValueType &); + static FunctionType to_function(ContextType, const ValueType &); + static double to_number(ContextType, const ValueType &); + static ObjectType to_object(ContextType, const ValueType &); + static String to_string(ContextType, const ValueType &); #define VALIDATED(return_t, type) \ - static return_t validated_to_##type(TContext ctx, const TValue &value, const char *name = nullptr) { \ + static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \ if (!is_##type(ctx, value)) { \ std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \ throw std::invalid_argument(prefix + " must be: " #type); \ @@ -109,13 +109,13 @@ struct Value { return to_##type(ctx, value); \ } - VALIDATED(TObject, array) + VALIDATED(ObjectType, array) VALIDATED(bool, boolean) - VALIDATED(TFunction, constructor) - VALIDATED(TObject, date) - VALIDATED(TFunction, function) + VALIDATED(FunctionType, constructor) + VALIDATED(ObjectType, date) + VALIDATED(FunctionType, function) VALIDATED(double, number) - VALIDATED(TObject, object) + VALIDATED(ObjectType, object) VALIDATED(String, string) #undef VALIDATED @@ -123,56 +123,56 @@ struct Value { template struct Function { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; - static TValue call(TContext, const TFunction &, const TObject &, size_t, const TValue[]); - static TValue call(TContext ctx, const TFunction &function, const TObject &this_object, const std::vector &arguments) { + static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]); + static ValueType call(ContextType ctx, const FunctionType &function, const ObjectType &this_object, const std::vector &arguments) { return call(ctx, function, this_object, arguments.size(), arguments.data()); } - static TObject construct(TContext, const TFunction &, size_t, const TValue[]); - static TValue construct(TContext ctx, const TFunction &function, const std::vector &arguments) { + static ObjectType construct(ContextType, const FunctionType &, size_t, const ValueType[]); + static ValueType construct(ContextType ctx, const FunctionType &function, const std::vector &arguments) { return construct(ctx, function, arguments.size(), arguments.data()); } }; template struct Object { - using TContext = typename T::Context; - using TFunction = typename T::Function; - using TObject = typename T::Object; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using FunctionType = typename T::Function; + using ObjectType = typename T::Object; + using ValueType = typename T::Value; public: - static TValue get_prototype(TContext, const TObject &); - static void set_prototype(TContext, const TObject &, const TValue &); + static ValueType get_prototype(ContextType, const ObjectType &); + static void set_prototype(ContextType, const ObjectType &, const ValueType &); - static bool has_property(TContext, const TObject &, const String &); - static bool has_property(TContext, const TObject &, uint32_t); - static TValue get_property(TContext, const TObject &, const String &); - static TValue get_property(TContext, const TObject &, uint32_t); - static void set_property(TContext, const TObject &, const String &, const TValue &, PropertyAttributes attributes = None); - static void set_property(TContext, const TObject &, uint32_t, const TValue &); - static std::vector> get_property_names(TContext, const TObject &); + static bool has_property(ContextType, const ObjectType &, const String &); + static bool has_property(ContextType, const ObjectType &, uint32_t); + static ValueType get_property(ContextType, const ObjectType &, const String &); + static ValueType get_property(ContextType, const ObjectType &, uint32_t); + static void set_property(ContextType, const ObjectType &, const String &, const ValueType &, PropertyAttributes attributes = None); + static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &); + static std::vector> get_property_names(ContextType, const ObjectType &); template - static TValue validated_get_property(TContext ctx, const TObject &object, const P &property, const char *message = nullptr) { + static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) { if (!has_property(ctx, object, property)) { throw std::out_of_range(message ?: "Object missing expected property: " + util::to_string(property)); } return get_property(ctx, object, property); } - static uint32_t validated_get_length(TContext ctx, const TObject &object) { + static uint32_t validated_get_length(ContextType ctx, const ObjectType &object) { static const String length_string = "length"; return Value::validated_to_number(ctx, get_property(ctx, object, length_string)); } #define VALIDATED(return_t, type) \ - static return_t validated_get_##type(TContext ctx, const TObject &object, const String &key, const char *message = nullptr) { \ + static return_t validated_get_##type(ContextType ctx, const ObjectType &object, const String &key, const char *message = nullptr) { \ try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \ } \ @@ -180,7 +180,7 @@ struct Object { throw message ? std::invalid_argument(message) : e; \ } \ } \ - static return_t validated_get_##type(TContext ctx, const TObject &object, uint32_t index, const char *message = nullptr) { \ + static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \ try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, index)); \ } \ @@ -189,78 +189,78 @@ struct Object { } \ } - VALIDATED(TObject, array) + VALIDATED(ObjectType, array) VALIDATED(bool, boolean) - VALIDATED(TFunction, constructor) - VALIDATED(TObject, date) - VALIDATED(TFunction, function) + VALIDATED(FunctionType, constructor) + VALIDATED(ObjectType, date) + VALIDATED(FunctionType, function) VALIDATED(double, number) - VALIDATED(TObject, object) + VALIDATED(ObjectType, object) VALIDATED(String, string) #undef VALIDATED - static TValue call_method(TContext ctx, const TObject &object, const String &name, uint32_t argc, const TValue arguments[]) { - TFunction method = validated_get_function(ctx, object, name); + static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, uint32_t argc, const ValueType arguments[]) { + FunctionType method = validated_get_function(ctx, object, name); return Function::call(ctx, method, object, argc, arguments); } - static TValue call_method(TContext ctx, const TObject &object, const String &name, const std::vector &arguments) { + static ValueType call_method(ContextType ctx, const ObjectType &object, const String &name, const std::vector &arguments) { return call_method(ctx, object, name, (uint32_t)arguments.size(), arguments.data()); } - static TObject create_empty(TContext); - static TObject create_array(TContext, uint32_t, const TValue[]); + static ObjectType create_empty(ContextType); + static ObjectType create_array(ContextType, uint32_t, const ValueType[]); - static TObject create_array(TContext ctx, const std::vector &values) { + static ObjectType create_array(ContextType ctx, const std::vector &values) { return create_array(ctx, (uint32_t)values.size(), values.data()); } - static TObject create_array(TContext ctx) { + static ObjectType create_array(ContextType ctx) { return create_array(ctx, 0, nullptr); } - static TObject create_date(TContext, double); + static ObjectType create_date(ContextType, double); template - static TObject create_instance(TContext, typename ClassType::Internal*); + static ObjectType create_instance(ContextType, typename ClassType::Internal*); template - static bool is_instance(TContext, const TObject &); + static bool is_instance(ContextType, const ObjectType &); template - static typename ClassType::Internal* get_internal(const TObject &); + static typename ClassType::Internal* get_internal(const ObjectType &); template - static void set_internal(const TObject &, typename ClassType::Internal*); + static void set_internal(const ObjectType &, typename ClassType::Internal*); }; -template +template class Protected { - operator TValue() const; - bool operator==(const TValue &) const; - bool operator!=(const TValue &) const; - bool operator==(const Protected &) const; - bool operator!=(const Protected &) const; + operator ValueType() const; + bool operator==(const ValueType &) const; + bool operator!=(const ValueType &) const; + bool operator==(const Protected &) const; + bool operator!=(const Protected &) const; }; template struct Exception : public std::runtime_error { - using TContext = typename T::Context; - using TValue = typename T::Value; + using ContextType = typename T::Context; + using ValueType = typename T::Value; - const Protected m_value; + const Protected m_value; - Exception(TContext ctx, const std::string &message) + Exception(ContextType ctx, const std::string &message) : std::runtime_error(message), m_value(value(ctx, message)) {} - Exception(TContext ctx, const TValue &val) + Exception(ContextType ctx, const ValueType &val) : std::runtime_error(std::string(Value::to_string(ctx, val))), m_value(ctx, val) {} - operator TValue() const { + operator ValueType() const { return m_value; } - static TValue value(TContext ctx, const std::string &message); + static ValueType value(ContextType ctx, const std::string &message); - static TValue value(TContext ctx, const std::exception &exp) { + static ValueType value(ContextType ctx, const std::exception &exp) { if (const Exception *js_exp = dynamic_cast *>(&exp)) { return *js_exp; } @@ -270,9 +270,9 @@ struct Exception : public std::runtime_error { template struct ReturnValue { - using TValue = typename T::Value; + using ValueType = typename T::Value; - void set(const TValue &); + void set(const ValueType &); void set(const std::string &); void set(bool); void set(double); From a0424c660061c87f15081bbba0efc3b9c39552ac Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 15:11:36 -0700 Subject: [PATCH 34/48] Fix some styling of exception handling in JS wrappers --- src/js_types.hpp | 4 ++-- src/jsc/jsc_class.hpp | 34 +++++++++++++++++++--------------- src/node/node_class.hpp | 16 ++++++++-------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/js_types.hpp b/src/js_types.hpp index 77e0db39..2147039e 100644 --- a/src/js_types.hpp +++ b/src/js_types.hpp @@ -176,7 +176,7 @@ struct Object { try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \ } \ - catch(std::invalid_argument &e) { \ + catch (std::invalid_argument &e) { \ throw message ? std::invalid_argument(message) : e; \ } \ } \ @@ -184,7 +184,7 @@ struct Object { try { \ return Value::validated_to_##type(ctx, get_property(ctx, object, index)); \ } \ - catch(std::invalid_argument &e) { \ + catch (std::invalid_argument &e) { \ throw message ? std::invalid_argument(message) : e; \ } \ } diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index e5612cda..f60080f6 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -230,7 +230,7 @@ inline JSObjectRef ObjectWrap::construct(JSContextRef ctx, JSObjectRe try { s_class.constructor(ctx, this_object, argc, arguments); } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); } return this_object; @@ -328,11 +328,12 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, jsc::ReturnValue return_value(ctx); try { F(ctx, this_object, argc, arguments, return_value); + return return_value; } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return nullptr; } - return return_value; } template @@ -340,11 +341,12 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSVa jsc::ReturnValue return_value(ctx); try { F(ctx, object, return_value); + return return_value; } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return nullptr; } - return return_value; } template @@ -353,10 +355,10 @@ bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef F(ctx, object, value); return true; } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return false; } - return false; } template @@ -364,15 +366,16 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef jsc::ReturnValue return_value(ctx); try { F(ctx, object, index, return_value); + return return_value; } catch (std::out_of_range &) { // Out-of-bounds index getters should just return undefined in JS. return jsc::Value::from_undefined(ctx); } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return nullptr; } - return return_value; } template @@ -380,10 +383,10 @@ bool wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef value try { return F(ctx, object, index, value); } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return false; } - return false; } template @@ -391,11 +394,12 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSVa jsc::ReturnValue return_value(ctx); try { F(ctx, object, property, return_value); + return return_value; } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return nullptr; } - return return_value; } template @@ -403,10 +407,10 @@ bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef try { return F(ctx, object, property, value); } - catch(std::exception &e) { + catch (std::exception &e) { *exception = jsc::Exception::value(ctx, e); + return false; } - return false; } template diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index bc5e0214..757e6228 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -239,7 +239,7 @@ inline void ObjectWrap::construct(Nan::NAN_METHOD_ARGS_TYPE info) { try { s_class.constructor(isolate, this_object, arguments.size(), arguments.data()); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -303,7 +303,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) { try { F(isolate, info.This(), arguments.size(), arguments.data(), return_value); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -315,7 +315,7 @@ void wrap(v8::Local property, Nan::NAN_GETTER_ARGS_TYPE info) { try { F(isolate, info.This(), return_value); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -326,7 +326,7 @@ void wrap(v8::Local property, v8::Local value, Nan::NAN_S try { F(isolate, info.This(), value); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -342,7 +342,7 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) { // Out-of-bounds index getters should just return undefined in JS. return_value.set_undefined(); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -356,7 +356,7 @@ void wrap(uint32_t index, v8::Local value, Nan::NAN_INDEX_SETTER_ARGS info.GetReturnValue().Set(value); } } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -368,7 +368,7 @@ void wrap(v8::Local property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE inf try { F(isolate, info.This(), property, return_value); } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } @@ -382,7 +382,7 @@ void wrap(v8::Local property, v8::Local value, Nan::NAN_P info.GetReturnValue().Set(value); } } - catch(std::exception &e) { + catch (std::exception &e) { Nan::ThrowError(node::Exception::value(isolate, e)); } } From fc49470b3cd11fcbe4ceb5f2b6066e89a0cb387c Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 15:11:56 -0700 Subject: [PATCH 35/48] Fix for failing nested transaction test --- src/js_realm.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index c3c21d32..6b7ecbee 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -459,17 +459,17 @@ void Realm::write(ContextType ctx, ObjectType this_object, size_t argc, const SharedRealm realm = *get_internal>(this_object); FunctionType callback = Value::validated_to_function(ctx, arguments[0]); + realm->begin_transaction(); + try { - realm->begin_transaction(); Function::call(ctx, callback, this_object, 0, nullptr); - realm->commit_transaction(); } - catch (std::exception &exp) { - if (realm->is_in_transaction()) { - realm->cancel_transaction(); - } - throw; + catch (std::exception &e) { + realm->cancel_transaction(); + throw e; } + + realm->commit_transaction(); } template From bc4bda876ccf2e6d7e9a290b3c363d9482831081 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 15:44:23 -0700 Subject: [PATCH 36/48] Remove binding.gyp file for now --- .gitignore | 1 + binding.gyp | 42 ------------------------------------------ 2 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 binding.gyp diff --git a/.gitignore b/.gitignore index f8a492f0..4c01386c 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,7 @@ build/ .rvmrc # node.js +/binding.gyp node_modules/ npm-debug.log diff --git a/binding.gyp b/binding.gyp deleted file mode 100644 index 77bcf04c..00000000 --- a/binding.gyp +++ /dev/null @@ -1,42 +0,0 @@ -{ - "targets": [ - { - "target_name": "realm", - "sources": [ - "src/js_realm.cpp", - "src/node/node_init.cpp", - "src/object-store/src/index_set.cpp", - "src/object-store/src/list.cpp", - "src/object-store/src/object_schema.cpp", - "src/object-store/src/object_store.cpp", - "src/object-store/src/results.cpp", - "src/object-store/src/schema.cpp", - "src/object-store/src/shared_realm.cpp", - "src/object-store/src/impl/async_query.cpp", - "src/object-store/src/impl/transact_log_handler.cpp", - "src/object-store/src/impl/realm_coordinator.cpp", - "src/object-store/src/impl/apple/external_commit_helper.cpp", - "src/object-store/src/impl/apple/weak_realm_notifier.cpp", - "src/object-store/src/parser/parser.cpp", - "src/object-store/src/parser/query_builder.cpp", - "src/ios/platform.mm" - ], - "include_dirs": [ - "core/include", - "node_modules/nan", - "src", - "src/object-store/src", - "src/object-store/src/impl", - "src/object-store/src/impl/apple", - "src/object-store/src/parser", - "src/object-store/external/pegtl" - ], - "cflags_cc": ["-DREALM_HAVE_CONFIG", "-fexceptions", "-frtti", "-std=c++14", "-g", "-O0"], - "ldflags": ["-Lcore", "-lrealm"], - "xcode_settings": { - "OTHER_CFLAGS": ["-mmacosx-version-min=10.8", "-DREALM_HAVE_CONFIG", "-fexceptions", "-frtti", "-std=c++14", "-stdlib=libc++", "-g", "-O0", "-Wno-mismatched-tags"], - "OTHER_LDFLAGS": ["-mmacosx-version-min=10.8", "-framework", "Foundation", "-Lcore", "-lrealm", "-std=c++14"] - } - } - ] -} From 73cfca7d812cbabaecfcd59850845b05f143c2e3 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 16:06:59 -0700 Subject: [PATCH 37/48] Fix for failing React Native test on iOS --- lib/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index b8aa002c..ec2a0e6b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -30,7 +30,9 @@ if (typeof Realm != 'undefined') { realmConstructor = require('./browser').default; // (exported as ES6 module) // eslint-disable-next-line } else if (typeof process == 'object' && ('' + process) == '[object process]') { - realmConstructor = require('bindings')('realm').Realm; + // Prevent React Native packager from seeing this module. + const bindings = 'bindings'; + realmConstructor = require(bindings)('realm').Realm; } else { throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!'); } From 8ff23b5411da174a238cedc538e199861147fde4 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 18 Apr 2016 18:30:55 -0700 Subject: [PATCH 38/48] Fix build issues on Android GCC was more strict with how we handled some types. --- react-native/android/src/main/jni/Android.mk | 9 ++------- src/android/io_realm_react_RealmReactModule.cpp | 10 ++++++---- src/android/jsc_override.cpp | 2 +- src/android/platform.cpp | 4 ++-- src/js_list.hpp | 8 ++++---- src/js_object_accessor.hpp | 4 ++-- src/js_realm.hpp | 12 ++++++------ src/js_realm_object.hpp | 12 ++++++------ src/js_results.hpp | 8 ++++---- src/js_schema.hpp | 6 +++--- 10 files changed, 36 insertions(+), 39 deletions(-) diff --git a/react-native/android/src/main/jni/Android.mk b/react-native/android/src/main/jni/Android.mk index 7d90ceb6..c3d61dd1 100644 --- a/react-native/android/src/main/jni/Android.mk +++ b/react-native/android/src/main/jni/Android.mk @@ -15,15 +15,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := librealmreact LOCAL_SRC_FILES := \ - src/js_collection.cpp \ - src/js_list.cpp \ - src/js_results.cpp \ - src/js_init.cpp \ src/js_realm.cpp \ - src/js_util.cpp \ - src/js_object.cpp \ - src/js_schema.cpp \ src/rpc.cpp \ + src/jsc/jsc_init.cpp \ src/android/platform.cpp \ src/android/io_realm_react_RealmReactModule.cpp \ src/android/jsc_override.cpp \ @@ -43,6 +37,7 @@ LOCAL_SRC_FILES := \ vendor/base64.cpp LOCAL_C_INCLUDES := src +LOCAL_C_INCLUDES += src/jsc LOCAL_C_INCLUDES += src/object-store/src LOCAL_C_INCLUDES += src/object-store/src/impl LOCAL_C_INCLUDES += src/object-store/src/parser diff --git a/src/android/io_realm_react_RealmReactModule.cpp b/src/android/io_realm_react_RealmReactModule.cpp index 864b7390..f94761f7 100644 --- a/src/android/io_realm_react_RealmReactModule.cpp +++ b/src/android/io_realm_react_RealmReactModule.cpp @@ -23,7 +23,9 @@ #include "rpc.hpp" #include "platform.hpp" -static realm_js::RPCServer *s_rpc_server; +using namespace realm::rpc; + +static RPCServer *s_rpc_server; extern bool realmContextInjected; JNIEXPORT void JNICALL Java_io_realm_react_RealmReactModule_setDefaultRealmFileDirectory @@ -46,7 +48,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactModule_setupChromeDebugMod if (s_rpc_server) { delete s_rpc_server; } - s_rpc_server = new realm_js::RPCServer(); + s_rpc_server = new RPCServer(); return (jlong)s_rpc_server; } @@ -55,8 +57,8 @@ JNIEXPORT jstring JNICALL Java_io_realm_react_RealmReactModule_processChromeDebu { const char* cmd = env->GetStringUTFChars(chrome_cmd, NULL); const char* args = env->GetStringUTFChars(chrome_args, NULL); - realm_js::json json = realm_js::json::parse(args); - realm_js::json response = s_rpc_server->perform_request(cmd, json); + json parsed_args = json::parse(args); + json response = s_rpc_server->perform_request(cmd, parsed_args); env->ReleaseStringUTFChars(chrome_cmd, cmd); env->ReleaseStringUTFChars(chrome_args, args); return env->NewStringUTF(response.dump().c_str()); diff --git a/src/android/jsc_override.cpp b/src/android/jsc_override.cpp index 8b49ef25..4ec0399e 100644 --- a/src/android/jsc_override.cpp +++ b/src/android/jsc_override.cpp @@ -23,7 +23,7 @@ #include #include -#include "js_init.h" +#include "jsc_init.h" #include "shared_realm.hpp" #include "realm_coordinator.hpp" diff --git a/src/android/platform.cpp b/src/android/platform.cpp index e4d29fa9..1ce4a029 100644 --- a/src/android/platform.cpp +++ b/src/android/platform.cpp @@ -16,11 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -#include "../platform.hpp" - #include "../js_init.h" #include #include +#include "../platform.hpp" + std::string s_default_realm_directory; namespace realm { diff --git a/src/js_list.hpp b/src/js_list.hpp index 63207841..628de5f1 100644 --- a/src/js_list.hpp +++ b/src/js_list.hpp @@ -37,9 +37,9 @@ class List { using ContextType = typename T::Context; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using Object = Object; - using Value = Value; - using ReturnValue = ReturnValue; + using Object = js::Object; + using Value = js::Value; + using ReturnValue = js::ReturnValue; public: static ObjectType create_instance(ContextType, realm::List &); @@ -62,7 +62,7 @@ class List { template struct ListClass : ClassDefinition> { - using List = List; + using List = js::List; std::string const name = "List"; diff --git a/src/js_object_accessor.hpp b/src/js_object_accessor.hpp index dd513247..9234b27f 100644 --- a/src/js_object_accessor.hpp +++ b/src/js_object_accessor.hpp @@ -30,8 +30,8 @@ struct NativeAccessor { using ContextType = typename T::Context; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using Object = Object; - using Value = Value; + using Object = js::Object; + using Value = js::Value; static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) { ObjectType object = Value::validated_to_object(ctx, dict); diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 6b7ecbee..17b02634 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -47,7 +47,7 @@ class RealmDelegate : public BindingContext { using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using Value = Value; + using Value = js::Value; using ObjectDefaultsMap = typename Schema::ObjectDefaultsMap; using ConstructorMap = typename Schema::ConstructorMap; @@ -121,10 +121,10 @@ class Realm { using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using String = String; - using Object = Object; - using Value = Value; - using ReturnValue = ReturnValue; + using String = js::String; + using Object = js::Object; + using Value = js::Value; + using ReturnValue = js::ReturnValue; using NativeAccessor = realm::NativeAccessor; public: @@ -189,7 +189,7 @@ class Realm { template struct RealmClass : ClassDefinition { - using Realm = Realm; + using Realm = js::Realm; std::string const name = "Realm"; diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index 48b4a97d..c5df2226 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -34,11 +34,11 @@ class RealmObject { using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using String = String; - using Value = Value; - using Object = Object; - using Function = Function; - using ReturnValue = ReturnValue; + using String = js::String; + using Value = js::Value; + using Object = js::Object; + using Function = js::Function; + using ReturnValue = js::ReturnValue; public: static ObjectType create_instance(ContextType, realm::Object &); @@ -50,7 +50,7 @@ class RealmObject { template struct RealmObjectClass : ClassDefinition { - using RealmObject = RealmObject; + using RealmObject = js::RealmObject; const std::string name = "RealmObject"; diff --git a/src/js_results.hpp b/src/js_results.hpp index 92b72c45..0318fb53 100644 --- a/src/js_results.hpp +++ b/src/js_results.hpp @@ -34,9 +34,9 @@ class Results { using ContextType = typename T::Context; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using Object = Object; - using Value = Value; - using ReturnValue = ReturnValue; + using Object = js::Object; + using Value = js::Value; + using ReturnValue = js::ReturnValue; public: static ObjectType create_instance(ContextType, const realm::Results &, bool live = true); @@ -60,7 +60,7 @@ class Results { template struct ResultsClass : ClassDefinition> { - using Results = Results; + using Results = js::Results; std::string const name = "Results"; diff --git a/src/js_schema.hpp b/src/js_schema.hpp index 31927abd..92e5c673 100644 --- a/src/js_schema.hpp +++ b/src/js_schema.hpp @@ -32,9 +32,9 @@ struct Schema { using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; - using String = String; - using Object = Object; - using Value = Value; + using String = js::String; + using Object = js::Object; + using Value = js::Value; using ObjectDefaults = std::map>; using ObjectDefaultsMap = std::map; From a05f7e22618316c595bc31f4522c0843ece5c0cb Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 01:38:46 -0700 Subject: [PATCH 39/48] Fix some eslint errors --- lib/index.js | 2 +- tests/index.js | 23 +++++++++++++++++++++-- tests/react-test-app/tests/index.js | 3 --- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/index.js b/lib/index.js index ec2a0e6b..fb4c7128 100644 --- a/lib/index.js +++ b/lib/index.js @@ -31,7 +31,7 @@ if (typeof Realm != 'undefined') { // eslint-disable-next-line } else if (typeof process == 'object' && ('' + process) == '[object process]') { // Prevent React Native packager from seeing this module. - const bindings = 'bindings'; + var bindings = 'bindings'; realmConstructor = require(bindings)('realm').Realm; } else { throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!'); diff --git a/tests/index.js b/tests/index.js index 48455ea8..2db13ab2 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,3 +1,24 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +/* eslint-env es6, node */ +/* eslint-disable no-console */ + 'use strict'; const mockery = require('mockery'); @@ -7,8 +28,6 @@ function runTests() { const testNames = RealmTests.getTestNames(); for (let suiteName in testNames) { - let testSuite = RealmTests[suiteName]; - console.log('Starting ' + suiteName); for (let testName of testNames[suiteName]) { diff --git a/tests/react-test-app/tests/index.js b/tests/react-test-app/tests/index.js index f40b104a..4e4e8aaa 100644 --- a/tests/react-test-app/tests/index.js +++ b/tests/react-test-app/tests/index.js @@ -19,7 +19,6 @@ 'use strict'; const React = require('react-native'); -const Realm = require('realm'); const RealmTests = require('realm-tests'); RealmTests.registerTests({ @@ -56,8 +55,6 @@ function runTests() { let testNames = RealmTests.getTestNames(); for (let suiteName in testNames) { - let testSuite = RealmTests[suiteName]; - console.log('Starting ' + suiteName); for (let testName of testNames[suiteName]) { From 541791fd8560966474ac226b92b775083aa887bc Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 03:18:02 -0700 Subject: [PATCH 40/48] Fix crash by properly protecting JSC values The previous implementation was lazy and ended up unprotecting values due to the move constructor being defaulted. --- src/js_realm.hpp | 2 +- src/jsc/jsc_protected.hpp | 60 ++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 17b02634..f6cdc13f 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -105,7 +105,7 @@ class RealmDelegate : public BindingContext { arguments[0] = realm_object; arguments[1] = Value::from_string(m_context, notification_name); - for (auto callback : m_notifications) { + for (auto &callback : m_notifications) { Function::call(m_context, callback, realm_object, 2, arguments); } } diff --git a/src/jsc/jsc_protected.hpp b/src/jsc/jsc_protected.hpp index 79f2b020..a330ba3a 100644 --- a/src/jsc/jsc_protected.hpp +++ b/src/jsc/jsc_protected.hpp @@ -21,52 +21,60 @@ #include "jsc_types.hpp" namespace realm { -namespace jsc { - -template -class Protected { - const MemberType m_value; - - public: - Protected(MemberType value) : m_value(value) {} - - operator MemberType() const { - return m_value; - } -}; - -} // jsc - namespace js { template<> -class Protected : public jsc::Protected { +class Protected { + JSGlobalContextRef m_context; + public: - Protected(JSGlobalContextRef ctx) : jsc::Protected(ctx) { - JSGlobalContextRetain(*this); + Protected(const Protected &other) : Protected(other.m_context) {} + Protected(Protected &&other) : m_context(other.m_context) { + other.m_context = nullptr; + } + Protected(JSGlobalContextRef ctx) : m_context(ctx) { + JSGlobalContextRetain(m_context); } ~Protected() { - JSGlobalContextRelease(*this); + if (m_context) { + JSGlobalContextRelease(m_context); + } + } + operator JSGlobalContextRef() const { + return m_context; } }; template<> -class Protected : public jsc::Protected { - const JSGlobalContextRef m_context; +class Protected { + JSGlobalContextRef m_context; + JSValueRef m_value; public: - Protected(JSContextRef ctx, JSValueRef value) : jsc::Protected(value), m_context(JSContextGetGlobalContext(ctx)) { - JSValueProtect(m_context, *this); + Protected(const Protected &other) : Protected(other.m_context, other.m_value) {} + Protected(Protected &&other) : m_context(other.m_context), m_value(other.m_value) { + other.m_context = nullptr; + other.m_value = nullptr; + } + Protected(JSContextRef ctx, JSValueRef value) : m_context(JSContextGetGlobalContext(ctx)), m_value(value) { + JSValueProtect(m_context, m_value); } ~Protected() { - JSValueUnprotect(m_context, *this); + if (m_value) { + JSValueUnprotect(m_context, m_value); + } + } + operator JSValueRef() const { + return m_value; } }; template<> class Protected : public Protected { public: - Protected(JSContextRef ctx, JSObjectRef object) : Protected(ctx, object) {} + Protected(const Protected &other) : Protected(other) {} + Protected(Protected &&other) : Protected(std::move(other)) {} + Protected(JSContextRef ctx, JSObjectRef value) : Protected(ctx, value) {} operator JSObjectRef() const { JSValueRef value = static_cast(*this); From 275334fa7eabd93df33283e30eddb9352afb8e46 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 03:24:15 -0700 Subject: [PATCH 41/48] Remove definition of Realm.Types from browser shim It's added in the top-level index.js file --- lib/browser/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/browser/index.js b/lib/browser/index.js index 03a809af..1830df93 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -169,9 +169,6 @@ Object.defineProperties(Realm, { Results: { value: Results, }, - Types: { - value: Object.freeze(propTypes), - }, defaultPath: { get: util.getterForProperty('defaultPath'), set: util.setterForProperty('defaultPath'), From 8e5afeccc9c6469f33866736d7458f44f3943c3e Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 03:58:23 -0700 Subject: [PATCH 42/48] Fix another crash involving protected values --- src/js_realm.hpp | 5 ++++- src/jsc/jsc_protected.hpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index f6cdc13f..b3cb1351 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -63,7 +63,10 @@ class RealmDelegate : public BindingContext { RealmDelegate(std::weak_ptr realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {} ~RealmDelegate() { - remove_all_notifications(); + // All protected values need to be unprotected while the context is retained. + m_defaults.clear(); + m_constructors.clear(); + m_notifications.clear(); } void add_notification(FunctionType notification) { diff --git a/src/jsc/jsc_protected.hpp b/src/jsc/jsc_protected.hpp index a330ba3a..39a8e198 100644 --- a/src/jsc/jsc_protected.hpp +++ b/src/jsc/jsc_protected.hpp @@ -32,7 +32,7 @@ class Protected { Protected(Protected &&other) : m_context(other.m_context) { other.m_context = nullptr; } - Protected(JSGlobalContextRef ctx) : m_context(ctx) { + explicit Protected(JSGlobalContextRef ctx) : m_context(ctx) { JSGlobalContextRetain(m_context); } ~Protected() { From 650950fe020f8ccbe96b471b68a333133134e7b8 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 10:49:08 -0700 Subject: [PATCH 43/48] Only use rpc namespace in debug mode --- react-native/ios/RealmReact/RealmReact.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-native/ios/RealmReact/RealmReact.mm b/react-native/ios/RealmReact/RealmReact.mm index 3299201c..b5b850b6 100644 --- a/react-native/ios/RealmReact/RealmReact.mm +++ b/react-native/ios/RealmReact/RealmReact.mm @@ -37,9 +37,9 @@ #import "rpc.hpp" #define WEB_SERVER_PORT 8082 -#endif using namespace realm::rpc; +#endif @interface NSObject () - (instancetype)initWithJSContext:(void *)context; From 2a588620707b4b1e2a0d910f74fefa62971a95a8 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 14:10:10 -0700 Subject: [PATCH 44/48] Add test script for Node --- .gitignore | 1 - scripts/test.sh | 21 ++++++++++++++++- src/node/binding.gyp | 54 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 src/node/binding.gyp diff --git a/.gitignore b/.gitignore index 4c01386c..f8a492f0 100644 --- a/.gitignore +++ b/.gitignore @@ -95,7 +95,6 @@ build/ .rvmrc # node.js -/binding.gyp node_modules/ npm-debug.log diff --git a/scripts/test.sh b/scripts/test.sh index 5aac53d6..07f0dc98 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -150,11 +150,30 @@ case "$TARGET" in echo "********* File location: $(pwd)/tests.xml *********"; cat tests.xml ;; +"node") + pushd src/node + node-gyp configure + + # Being explicit about debug mode rather than relying on defaults. + if [[ $CONFIGURATION == 'Debug' ]]; then + node-gyp build --debug + else + node-gyp build --no-debug + fi + + popd + + # Link to the appropriate module in the build directory. + mkdir -p build + ln -fs "../src/node/build/$CONFIGURATION/realm.node" build + + node tests + ;; "object-store") pushd src/object-store cmake -DCMAKE_BUILD_TYPE=$CONFIGURATION . make run-tests -;; + ;; *) echo "Invalid target '${TARGET}'" exit 1 diff --git a/src/node/binding.gyp b/src/node/binding.gyp new file mode 100644 index 00000000..e7708d9f --- /dev/null +++ b/src/node/binding.gyp @@ -0,0 +1,54 @@ +{ + "targets": [ + { + "target_name": "realm", + "sources": [ + "node_init.cpp", + "../js_realm.cpp", + "../ios/platform.mm", + "../object-store/src/index_set.cpp", + "../object-store/src/list.cpp", + "../object-store/src/object_schema.cpp", + "../object-store/src/object_store.cpp", + "../object-store/src/results.cpp", + "../object-store/src/schema.cpp", + "../object-store/src/shared_realm.cpp", + "../object-store/src/impl/async_query.cpp", + "../object-store/src/impl/transact_log_handler.cpp", + "../object-store/src/impl/realm_coordinator.cpp", + "../object-store/src/impl/apple/external_commit_helper.cpp", + "../object-store/src/impl/apple/weak_realm_notifier.cpp", + "../object-store/src/parser/parser.cpp", + "../object-store/src/parser/query_builder.cpp" + ], + "include_dirs": [ + "..", + "../object-store/src", + "../object-store/src/impl", + "../object-store/src/impl/apple", + "../object-store/src/parser", + "../object-store/external/pegtl", + "../../core/include", + "../../node_modules/nan" + ], + "library_dirs": [ + "$(srcdir)/../../core" + ], + "defines": ["REALM_HAVE_CONFIG"], + "cflags_cc": ["-fexceptions", "-frtti", "-std=c++14"], + "ldflags": ["-lrealm"], + "xcode_settings": { + "CLANG_CXX_LANGUAGE_STANDARD": "c++14", + "CLANG_CXX_LIBRARY": "libc++", + "MACOSX_DEPLOYMENT_TARGET": "10.8", + "OTHER_CPLUSPLUSFLAGS": ["-fexceptions", "-frtti"], + "OTHER_LDFLAGS": ["-lrealm", "-framework", "Foundation"] + }, + "configurations": { + "Debug": { + "defines": ["DEBUG=1"] + } + } + } + ] +} From d5aad959ce8deeb21a60953aceaed4e8429d2ca0 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 16:40:48 -0700 Subject: [PATCH 45/48] Run `npm install` for node tests --- scripts/test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test.sh b/scripts/test.sh index 07f0dc98..a96e489e 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -151,6 +151,8 @@ case "$TARGET" in cat tests.xml ;; "node") + npm install + pushd src/node node-gyp configure From ece554e58f6ab0bda318bb5857c48fded0a05bcb Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 16:53:49 -0700 Subject: [PATCH 46/48] Exit with an error code for failures of node tests --- tests/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/index.js b/tests/index.js index 2db13ab2..67d9abc8 100644 --- a/tests/index.js +++ b/tests/index.js @@ -26,6 +26,7 @@ const mockery = require('mockery'); function runTests() { const RealmTests = require('./js'); const testNames = RealmTests.getTestNames(); + let passed = true; for (let suiteName in testNames) { console.log('Starting ' + suiteName); @@ -40,12 +41,15 @@ function runTests() { catch (e) { console.warn('- ' + testName); console.error(e.message, e.stack); + passed = false; } finally { RealmTests.runTest(suiteName, 'afterEach'); } } } + + return passed; } if (require.main == module) { @@ -53,5 +57,7 @@ if (require.main == module) { mockery.warnOnUnregistered(false); mockery.registerMock('realm', require('..')); - runTests(); + if (!runTests()) { + process.exit(1); + } } From 648748cd24e67a88f96a4261b06c042a121638e0 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 16:57:52 -0700 Subject: [PATCH 47/48] Add node_modules to PATH in test script --- scripts/test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/test.sh b/scripts/test.sh index a96e489e..fb0b06e9 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -12,6 +12,9 @@ SRCROOT=$(cd "$(dirname "$0")/.." && pwd) # Start current working directory at the root of the project. cd "$SRCROOT" +# Add node_modules to PATH just in case we weren't called from `npm test` +PATH="$PWD/node_modules/.bin:$PATH" + if [[ $TARGET = *-android ]]; then # Inform the prepublish script to build Android modules. export REALM_BUILD_ANDROID=1 From 7abdacdf06eb207f888ca4e0ca7b6459f4045d5c Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Tue, 19 Apr 2016 17:10:14 -0700 Subject: [PATCH 48/48] Make sure core is downloaded for node tests --- scripts/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test.sh b/scripts/test.sh index fb0b06e9..e80e8e73 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -155,6 +155,7 @@ case "$TARGET" in ;; "node") npm install + scripts/download-core.sh pushd src/node node-gyp configure