start refactoring realm

This commit is contained in:
Ari Lazier 2016-03-28 13:21:36 -07:00
parent 20bac8e2c7
commit 9626842f8e
12 changed files with 780 additions and 435 deletions

View File

@ -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 = "<group>"; };
029048011C0428DF00ABDED4 /* js_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_init.cpp; sourceTree = "<group>"; };
029048021C0428DF00ABDED4 /* js_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = js_init.h; sourceTree = "<group>"; };
029048031C0428DF00ABDED4 /* js_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_list.cpp; sourceTree = "<group>"; };
029048041C0428DF00ABDED4 /* js_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_list.hpp; sourceTree = "<group>"; };
029048051C0428DF00ABDED4 /* js_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_object.cpp; sourceTree = "<group>"; };
029048061C0428DF00ABDED4 /* js_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_object.hpp; sourceTree = "<group>"; };
@ -124,6 +123,8 @@
029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = "<group>"; };
0290483A1C042EE200ABDED4 /* RealmJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmJS.h; sourceTree = "<group>"; };
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 = "<group>"; };
02AFE5881CA9B23400953DA3 /* jsc_list.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_list.hpp; path = jsc/jsc_list.hpp; sourceTree = "<group>"; };
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 */,

68
src/js_compat.hpp Normal file
View File

@ -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 <string>
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<typename T>
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);
}}

View File

@ -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"

View File

@ -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 <assert.h>
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm;
void RJSSetReturnUndefined(JSContextRef ctx, JSValueRef &returnObject) {
returnObject = JSValueMakeUndefined(ctx);
}
template<typename T>
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<List *>(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<List *>(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<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
size_t size = list->size();
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
long index = std::min<long>(RJSValidatedValueToNumber(ctx, arguments[0]), size);
if (index < 0) {
index = std::max<long>(size + index, 0);
}
long remove;
if (argumentCount < 2) {
remove = size - index;
}
else {
remove = std::max<long>(RJSValidatedValueToNumber(ctx, arguments[1]), 0);
remove = std::min<long>(remove, size - index);
}
std::vector<ReturnType> 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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentRange(argumentCount, 1, 2);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<List *>(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 *>("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass());
return s_listClass;
}

View File

@ -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 <assert.h>
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm;
template<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListPush(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListPop(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListUnshift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListShift(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListSplice(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
size_t size = list->size();
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
long index = std::min<long>(RJSValidatedValueToNumber(ctx, arguments[0]), size);
if (index < 0) {
index = std::max<long>(size + index, 0);
}
long remove;
if (argumentCount < 2) {
remove = size - index;
}
else {
remove = std::max<long>(RJSValidatedValueToNumber(ctx, arguments[1]), 0);
remove = std::min<long>(remove, size - index);
}
std::vector<ReturnType> 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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListStaticResults(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListFiltered(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void ListSorted(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentRange(argumentCount, 1, 2);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
returnObject = RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments);
}
catch (std::exception &exp) {
RJSSetException(ctx, exceptionObject, exp);
}
}

View File

@ -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) {

View File

@ -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 <set>
#include <cassert>
@ -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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void RealmObjects(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try {
RJSValidateArgumentCount(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void RealmCreateObject(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try {
RJSValidateArgumentRange(argumentCount, 2, 3);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<JSValueRef>(ctx, sharedRealm, *object_schema, object, update));
returnObject = RJSObjectCreate(ctx, Object::create<JSValueRef>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
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<Object *>(RJSValidatedValueToObject(ctx, arguments[0]));
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
if (!realm->is_in_transaction()) {
throw std::runtime_error("Can only delete objects within a transaction.");
}
JSObjectRef arg = RJSValidatedValueToObject(ctx, arguments[0]);
if (RJSValueIsObjectOfClass(ctx, arg, RJSObjectClass())) {
Object *object = RJSGetInternal<Object *>(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.");
}
return NULL;
Object *object = RJSGetInternal<Object *>(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<Results *>(arg);
results->clear();
}
else if(RJSValueIsObjectOfClass(ctx, arg, RJSListClass())) {
List *list = RJSGetInternal<List *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void RealmDeleteAll(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try {
RJSValidateArgumentCount(argumentCount, 0);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void RealmWrite(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) {
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
try {
RJSValidateArgumentCount(argumentCount, 1);
JSObjectRef object = RJSValidatedValueToFunction(ctx, arguments[0]);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
realm->begin_transaction();
JSObjectCallAsFunction(ctx, object, thisObject, 0, NULL, jsException);
if (*jsException) {
realm->cancel_transaction();
}
else {
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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
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<SharedRealm *>(thisObject);
static_cast<RJSRealmDelegate *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
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<SharedRealm *>(thisObject);
static_cast<RJSRealmDelegate *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
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,29 +482,32 @@ JSValueRef RealmRemoveAllListeners(JSContextRef ctx, JSObjectRef function, JSObj
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
static_cast<RJSRealmDelegate *>(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<typename ContextType, typename ThisType, typename ArgumentsType, typename ReturnType, typename ExceptionType>
void RealmClose(ContextType ctx, ThisType thisObject, size_t argumentCount, const ArgumentsType &arguments, ReturnType &returnObject, ExceptionType &exceptionObject) { try {
RJSValidateArgumentCount(argumentCount, 0);
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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},

View File

@ -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<typename T>
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<typename T>
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);
}

117
src/jsc/jsc_list.cpp Normal file
View File

@ -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 <assert.h>
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm;
JSValueRef ListGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(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<List *>(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<List *>(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<List *>(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 *>("List", ListGetProperty, ListSetProperty, RJSListFuncs, ListPropertyNames, RJSCollectionClass());
return s_listClass;
}

25
src/jsc/jsc_list.hpp Normal file
View File

@ -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);

251
src/jsc/jsc_util.hpp Normal file
View File

@ -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 <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <math.h>
#include <string>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include "property.hpp"
#include "schema.hpp"
namespace realm {
namespace js {
template<typename T>
inline void RJSFinalize(JSObjectRef object) {
delete static_cast<T>(JSObjectGetPrivate(object));
JSObjectSetPrivate(object, NULL);
}
template<typename T>
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<typename T>
inline T RJSGetInternal(JSObjectRef jsObject) {
return static_cast<T>(JSObjectGetPrivate(jsObject));
}
template<typename T>
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<T>;
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<typename T>
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<long>(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;
}
}}

View File

@ -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"