tmp
This commit is contained in:
parent
5b2a59d5c6
commit
016d723731
|
@ -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 = "<group>"; };
|
||||
025678951CAB392000FB8501 /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = types.hpp; path = jsc/types.hpp; sourceTree = "<group>"; };
|
||||
025678961CAB478800FB8501 /* jsc_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = jsc_realm.cpp; path = jsc/jsc_realm.cpp; sourceTree = "<group>"; };
|
||||
025678971CAB478800FB8501 /* jsc_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = jsc_realm.hpp; path = jsc/jsc_realm.hpp; sourceTree = "<group>"; };
|
||||
0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = "<group>"; };
|
||||
0270BC7A1B7D020100010E03 /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ios/RealmJSTests.h; sourceTree = "<group>"; };
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
426
src/js_realm.cpp
426
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 <cassert>
|
||||
|
||||
|
||||
using namespace realm;
|
||||
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
|
||||
|
||||
// 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<std::string, realm::ObjectDefaults> defaults;
|
||||
std::map<std::string, JSObjectRef> 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<char>(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<jsc::Types>(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<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
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<char>(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<SharedRealm *>(object)->get()->config().path);
|
||||
}
|
||||
|
||||
static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion");
|
||||
if (JSStringIsEqual(propertyName, s_schemaVersion)) {
|
||||
return JSValueMakeNumber(ctx, RJSGetInternal<SharedRealm *>(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<jsc::Types>(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<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmObjects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try {
|
||||
RJSValidateArgumentCount(argumentCount, 1);
|
||||
|
||||
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
|
||||
std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]);
|
||||
returnObject = RJSResultsCreate(ctx, sharedRealm, className);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmCreateObject(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentRange(argumentCount, 2, 3);
|
||||
|
||||
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<ValueType>(ctx, sharedRealm, *object_schema, object, update));
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmDelete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try {
|
||||
RJSValidateArgumentCount(argumentCount, 1);
|
||||
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<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 (!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<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) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmDeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try {
|
||||
RJSValidateArgumentCount(argumentCount, 0);
|
||||
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmWrite(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->add_notification(callback);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->remove_notification(callback);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->remove_all_notifications();
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
void RealmClose(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) { try {
|
||||
RJSValidateArgumentCount(argumentCount, 0);
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<SharedRealm *>("Realm", RealmGetProperty, NULL, RJSRealmFuncs);
|
||||
return s_realmClass;
|
||||
}
|
||||
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
JSClassRef RealmClass() { return RJSRealmClass(); };
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
245
src/js_realm.hpp
245
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 <map>
|
||||
#include <set>
|
||||
|
||||
|
@ -94,7 +96,7 @@ private:
|
|||
if (!realm) {
|
||||
throw std::runtime_error("Realm no longer exists");
|
||||
}
|
||||
ObjectType realm_object = WrapObject<SharedRealm *>(m_context, realm::js::RealmClass(), new SharedRealm(realm));
|
||||
ObjectType realm_object = WrapObject<SharedRealm *>(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<T> *get_delegate(Realm *realm) {
|
|||
return dynamic_cast<realm::js::RealmDelegate<T> *>(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<jsc::Types>(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<typename T>
|
||||
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<typename T>
|
||||
void Realm<T>::Objects(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentCount(argumentCount, 1);
|
||||
|
||||
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
|
||||
std::string className = RJSValidatedObjectTypeForValue(sharedRealm, ctx, arguments[0]);
|
||||
returnObject = RJSResultsCreate(ctx, sharedRealm, className);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::Create(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentRange(argumentCount, 2, 3);
|
||||
|
||||
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(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<ValueType>(ctx, sharedRealm, *object_schema, object, update));
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::Delete(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentCount(argumentCount, 1);
|
||||
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<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 (!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<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, realm::js::results_class())) {
|
||||
Results *results = RJSGetInternal<Results *>(arg);
|
||||
results->clear();
|
||||
}
|
||||
else if(RJSValueIsObjectOfClass(ctx, arg, list_class())) {
|
||||
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) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::DeleteAll(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentCount(argumentCount, 0);
|
||||
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<typename T>
|
||||
void Realm<T>::Write(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<typename T>
|
||||
void Realm<T>::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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->add_notification(callback);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->remove_notification(callback);
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::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<SharedRealm *>(thisObject);
|
||||
static_cast<js::RealmDelegate<jsc::Types> *>(realm->m_binding_context.get())->remove_all_notifications();
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Realm<T>::Close(ContextType ctx, ObjectType thisObject, size_t argumentCount, const ValueType arguments[], ReturnType &returnObject, ExceptionType &exceptionObject) {
|
||||
try {
|
||||
RJSValidateArgumentCount(argumentCount, 0);
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(thisObject);
|
||||
realm->close();
|
||||
}
|
||||
catch (std::exception &exp) {
|
||||
RJSSetException(ctx, exceptionObject, exp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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<Results *>(ctx, js::ResultsClass(), results);
|
||||
return js::WrapObject<Results *>(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<Results *>(ctx, js::ResultsClass(), results);
|
||||
return js::WrapObject<Results *>(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(); };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<class T>
|
||||
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();
|
||||
|
||||
}}
|
||||
|
|
|
@ -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 <set>
|
||||
#include <cassert>
|
||||
|
||||
using namespace realm;
|
||||
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
|
||||
|
||||
class RJSRealmDelegate : public BindingContext {
|
||||
public:
|
||||
virtual void changes_available() {
|
||||
assert(0);
|
||||
}
|
||||
virtual void did_change(std::vector<ObserverState> const& observers, std::vector<void*> const& invalidated) {
|
||||
notify("change");
|
||||
}
|
||||
virtual std::vector<ObserverState> get_observed_rows() {
|
||||
return std::vector<ObserverState>();
|
||||
}
|
||||
virtual void will_change(std::vector<ObserverState> const& observers,
|
||||
std::vector<void*> 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<std::string, ObjectDefaults> m_defaults;
|
||||
std::map<std::string, JSObjectRef> m_constructors;
|
||||
|
||||
private:
|
||||
std::set<JSObjectRef> 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<SharedRealm *>(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<std::string, ObjectDefaults> &RJSDefaults(Realm *realm) {
|
||||
return static_cast<RJSRealmDelegate *>(realm->m_binding_context.get())->m_defaults;
|
||||
}
|
||||
|
||||
std::map<std::string, JSObjectRef> &RJSConstructors(Realm *realm) {
|
||||
return static_cast<RJSRealmDelegate *>(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<std::string, realm::ObjectDefaults> defaults;
|
||||
std::map<std::string, JSObjectRef> 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<char>(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<SharedRealm *>(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<char>(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<SharedRealm *>(object)->get()->config().path);
|
||||
}
|
||||
|
||||
static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion");
|
||||
if (JSStringIsEqual(propertyName, s_schemaVersion)) {
|
||||
return JSValueMakeNumber(ctx, RJSGetInternal<SharedRealm *>(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<SharedRealm *>(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<SharedRealm *>(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<JSValueRef>(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<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.");
|
||||
}
|
||||
|
||||
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<SharedRealm *>(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<SharedRealm *>(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<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;
|
||||
}
|
||||
}
|
||||
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
|
||||
JSValueRef RealmClose(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
|
||||
try {
|
||||
RJSValidateArgumentCount(argumentCount, 0);
|
||||
SharedRealm realm = *RJSGetInternal<SharedRealm *>(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<SharedRealm *>("Realm", RealmGetProperty, NULL, RJSRealmFuncs);
|
||||
return s_realmClass;
|
||||
}
|
|
@ -97,7 +97,7 @@ WRAP_CLASS_METHOD(RJSList, Filtered)
|
|||
WRAP_CLASS_METHOD(RJSList, Sorted)
|
||||
|
||||
JSObjectRef RJSListCreate(JSContextRef ctx, List &list) {
|
||||
return realm::js::WrapObject<List *>(ctx, realm::js::ListClass(), new List(list));
|
||||
return realm::js::WrapObject<List *>(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(); };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <cassert>
|
||||
|
||||
using namespace realm;
|
||||
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
|
||||
|
||||
|
||||
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<std::string, realm::ObjectDefaults> defaults;
|
||||
std::map<std::string, JSObjectRef> 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<char>(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<jsc::Types>(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<typename ContextType, typename ObjectType, typename ValueType, typename ReturnType, typename ExceptionType>
|
||||
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<char>(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<SharedRealm *>(object)->get()->config().path);
|
||||
}
|
||||
|
||||
static JSStringRef s_schemaVersion = JSStringCreateWithUTF8CString("schemaVersion");
|
||||
if (JSStringIsEqual(propertyName, s_schemaVersion)) {
|
||||
return JSValueMakeNumber(ctx, RJSGetInternal<SharedRealm *>(object)->get()->config().schema_version);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
using RJSRealm = realm::js::Realm<realm::jsc::Types>;
|
||||
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<SharedRealm *>("Realm", RealmGetProperty, NULL, RJSRealmFuncs);
|
||||
return s_realmClass;
|
||||
}
|
||||
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
JSClassRef realm_class() { return RJSRealmClass(); };
|
||||
}
|
||||
}
|
|
@ -18,19 +18,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "js_util.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace realm {
|
||||
class Realm;
|
||||
using ObjectDefaults = std::map<std::string, JSValueRef>;
|
||||
}
|
||||
#include "types.hpp"
|
||||
|
||||
JSClassRef RJSRealmClass();
|
||||
JSClassRef RJSRealmConstructorClass();
|
||||
|
||||
std::string RJSDefaultPath();
|
||||
void RJSSetDefaultPath(std::string path);
|
||||
|
||||
std::map<std::string, realm::ObjectDefaults> &RJSDefaults(realm::Realm *realm);
|
||||
std::map<std::string, JSObjectRef> &RJSConstructors(realm::Realm *realm);
|
Loading…
Reference in New Issue