Merge pull request #393 from realm/sk-node

Support Alternative JS Engines
This commit is contained in:
Scott Kyle 2016-04-19 17:26:14 -07:00
commit 7ceac728eb
62 changed files with 4966 additions and 2233 deletions

View File

@ -169,9 +169,6 @@ Object.defineProperties(Realm, {
Results: {
value: Results,
},
Types: {
value: Object.freeze(propTypes),
},
defaultPath: {
get: util.getterForProperty('defaultPath'),
set: util.setterForProperty('defaultPath'),

View File

@ -24,9 +24,15 @@ var realmConstructor;
if (typeof Realm != 'undefined') {
// The global Realm constructor should be available on device (using JavaScriptCore).
realmConstructor = Realm; // eslint-disable-line no-undef
} else if (typeof navigator != 'undefined' && navigator.userAgent) { // eslint-disable-line no-undef
// eslint-disable-next-line
} else if (typeof navigator != 'undefined' && navigator.userAgent) {
// The userAgent will be defined when running in a browser (such as Chrome debugging mode).
realmConstructor = require('./browser').default; // (exported as ES6 module)
// eslint-disable-next-line
} else if (typeof process == 'object' && ('' + process) == '[object process]') {
// Prevent React Native packager from seeing this module.
var bindings = 'bindings';
realmConstructor = require(bindings)('realm').Realm;
} else {
throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
}
@ -34,4 +40,19 @@ if (typeof Realm != 'undefined') {
// Add the specified Array methods to the Collection prototype.
Object.defineProperties(realmConstructor.Collection.prototype, arrayMethods);
// TODO: Remove this now useless object.
Object.defineProperty(realmConstructor, 'Types', {
value: Object.freeze({
'BOOL': 'bool',
'INT': 'int',
'FLOAT': 'float',
'DOUBLE': 'double',
'STRING': 'string',
'DATE': 'date',
'DATA': 'data',
'OBJECT': 'object',
'LIST': 'list',
})
});
module.exports = realmConstructor;

View File

@ -47,6 +47,10 @@
"prepublish": "scripts/prepublish.sh"
},
"dependencies": {
"bindings": "^1.2.1",
"mockery": "^1.6.2",
"nan": "^2.2.1",
"node-gyp": "^3.3.1",
"rnpm": "1.5.2",
"xcode": "0.8.4"
},

View File

@ -15,15 +15,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := librealmreact
LOCAL_SRC_FILES := \
src/js_collection.cpp \
src/js_list.cpp \
src/js_results.cpp \
src/js_init.cpp \
src/js_realm.cpp \
src/js_util.cpp \
src/js_object.cpp \
src/js_schema.cpp \
src/rpc.cpp \
src/jsc/jsc_init.cpp \
src/android/platform.cpp \
src/android/io_realm_react_RealmReactModule.cpp \
src/android/jsc_override.cpp \
@ -43,6 +37,7 @@ LOCAL_SRC_FILES := \
vendor/base64.cpp
LOCAL_C_INCLUDES := src
LOCAL_C_INCLUDES += src/jsc
LOCAL_C_INCLUDES += src/object-store/src
LOCAL_C_INCLUDES += src/object-store/src/impl
LOCAL_C_INCLUDES += src/object-store/src/parser

View File

@ -20,7 +20,7 @@
#import "RealmAnalytics.h"
#import "RCTBridge.h"
#import "js_init.h"
#import "jsc_init.h"
#import "shared_realm.hpp"
#import "realm_coordinator.hpp"
@ -37,6 +37,8 @@
#import "rpc.hpp"
#define WEB_SERVER_PORT 8082
using namespace realm::rpc;
#endif
@interface NSObject ()
@ -82,7 +84,7 @@ extern "C" JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executo
#if DEBUG
GCDWebServer *_webServer;
std::unique_ptr<realm_js::RPCServer> _rpcServer;
std::unique_ptr<RPCServer> _rpcServer;
#endif
}
@ -204,7 +206,7 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
- (void)startRPC {
[GCDWebServer setLogLevel:3];
_webServer = [[GCDWebServer alloc] init];
_rpcServer = std::make_unique<realm_js::RPCServer>();
_rpcServer = std::make_unique<RPCServer>();
__weak __typeof__(self) weakSelf = self;
// Add a handler to respond to POST requests on any URL
@ -219,7 +221,7 @@ RCT_REMAP_METHOD(emit, emitEvent:(NSString *)eventName withObject:(id)object) {
RealmReact *self = weakSelf;
if (self) {
if (_rpcServer) {
realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]);
json args = json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]);
std::string responseText = _rpcServer->perform_request(request.path.UTF8String, args).dump();
responseData = [NSData dataWithBytes:responseText.c_str() length:responseText.length()];
return;

View File

@ -12,6 +12,9 @@ SRCROOT=$(cd "$(dirname "$0")/.." && pwd)
# Start current working directory at the root of the project.
cd "$SRCROOT"
# Add node_modules to PATH just in case we weren't called from `npm test`
PATH="$PWD/node_modules/.bin:$PATH"
if [[ $TARGET = *-android ]]; then
# Inform the prepublish script to build Android modules.
export REALM_BUILD_ANDROID=1
@ -150,11 +153,33 @@ case "$TARGET" in
echo "********* File location: $(pwd)/tests.xml *********";
cat tests.xml
;;
"node")
npm install
scripts/download-core.sh
pushd src/node
node-gyp configure
# Being explicit about debug mode rather than relying on defaults.
if [[ $CONFIGURATION == 'Debug' ]]; then
node-gyp build --debug
else
node-gyp build --no-debug
fi
popd
# Link to the appropriate module in the build directory.
mkdir -p build
ln -fs "../src/node/build/$CONFIGURATION/realm.node" build
node tests
;;
"object-store")
pushd src/object-store
cmake -DCMAKE_BUILD_TYPE=$CONFIGURATION .
make run-tests
;;
;;
*)
echo "Invalid target '${TARGET}'"
exit 1

View File

@ -23,7 +23,9 @@
#include "rpc.hpp"
#include "platform.hpp"
static realm_js::RPCServer *s_rpc_server;
using namespace realm::rpc;
static RPCServer *s_rpc_server;
extern bool realmContextInjected;
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactModule_setDefaultRealmFileDirectory
@ -46,7 +48,7 @@ JNIEXPORT jlong JNICALL Java_io_realm_react_RealmReactModule_setupChromeDebugMod
if (s_rpc_server) {
delete s_rpc_server;
}
s_rpc_server = new realm_js::RPCServer();
s_rpc_server = new RPCServer();
return (jlong)s_rpc_server;
}
@ -55,8 +57,8 @@ JNIEXPORT jstring JNICALL Java_io_realm_react_RealmReactModule_processChromeDebu
{
const char* cmd = env->GetStringUTFChars(chrome_cmd, NULL);
const char* args = env->GetStringUTFChars(chrome_args, NULL);
realm_js::json json = realm_js::json::parse(args);
realm_js::json response = s_rpc_server->perform_request(cmd, json);
json parsed_args = json::parse(args);
json response = s_rpc_server->perform_request(cmd, parsed_args);
env->ReleaseStringUTFChars(chrome_cmd, cmd);
env->ReleaseStringUTFChars(chrome_args, args);
return env->NewStringUTF(response.dump().c_str());

View File

@ -23,7 +23,7 @@
#include <mutex>
#include <JavaScriptCore/JSContextRef.h>
#include "js_init.h"
#include "jsc_init.h"
#include "shared_realm.hpp"
#include "realm_coordinator.hpp"

View File

@ -16,11 +16,11 @@
//
////////////////////////////////////////////////////////////////////////////
#include "../platform.hpp"
#include "../js_init.h"
#include <string>
#include <stdlib.h>
#include "../platform.hpp"
std::string s_default_realm_directory;
namespace realm {

View File

@ -19,6 +19,6 @@
#ifndef REALM_JS_H
#define REALM_JS_H
#include <RealmJS/js_init.h>
#include <RealmJS/jsc_init.h>
#endif /* REALM_JS_H */

View File

@ -10,13 +10,12 @@
02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; };
0270BC821B7D020100010E03 /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */; };
0270BC871B7D023200010E03 /* RealmJS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02B58CB11AE99CEC009B348C /* RealmJS.framework */; };
029048121C0428DF00ABDED4 /* js_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* js_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
029048121C0428DF00ABDED4 /* jsc_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 029048021C0428DF00ABDED4 /* jsc_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
029048141C0428DF00ABDED4 /* js_list.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048041C0428DF00ABDED4 /* js_list.hpp */; };
029048161C0428DF00ABDED4 /* js_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048061C0428DF00ABDED4 /* js_object.hpp */; };
029048161C0428DF00ABDED4 /* js_realm_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048061C0428DF00ABDED4 /* js_realm_object.hpp */; };
029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048081C0428DF00ABDED4 /* js_realm.hpp */; };
0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480A1C0428DF00ABDED4 /* js_results.hpp */; };
0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480C1C0428DF00ABDED4 /* js_schema.hpp */; };
0290481E1C0428DF00ABDED4 /* js_util.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0290480E1C0428DF00ABDED4 /* js_util.hpp */; };
029048201C0428DF00ABDED4 /* rpc.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048101C0428DF00ABDED4 /* rpc.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
029048371C042A3C00ABDED4 /* platform.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 029048351C042A3C00ABDED4 /* platform.hpp */; };
0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0290483A1C042EE200ABDED4 /* RealmJS.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -36,14 +35,29 @@
02F59EE11C88F2BB007F774C /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; };
02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; };
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EAF1C88F17D007F774C /* index_set.cpp */; };
F60102D51CBB96AE00EC01BA /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB11C88F17D007F774C /* list.cpp */; };
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB41C88F17D007F774C /* object_schema.cpp */; };
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB61C88F17D007F774C /* object_store.cpp */; };
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EB91C88F17D007F774C /* results.cpp */; };
F60102D91CBB96C100EC01BA /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBB1C88F17D007F774C /* schema.cpp */; };
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EBD1C88F17D007F774C /* shared_realm.cpp */; };
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC61C88F190007F774C /* parser.cpp */; };
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC81C88F190007F774C /* query_builder.cpp */; };
F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */; };
F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */; };
F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; };
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; };
F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; };
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; };
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
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 */; };
F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; };
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; };
F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; };
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; };
F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048091C0428DF00ABDED4 /* js_results.cpp */; };
F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480B1C0428DF00ABDED4 /* js_schema.cpp */; };
F63FF2CC1C12469E00B3B8E0 /* js_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480D1C0428DF00ABDED4 /* js_util.cpp */; };
F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; };
F63FF2DE1C1565EA00B3B8E0 /* RealmJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */; };
F63FF2E11C1591EC00B3B8E0 /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; };
@ -65,8 +79,6 @@
F68A278C1BC2722A0063D40A /* RJSModuleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */; };
F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6BB7DF01BF681BC00D0A69E /* base64.hpp */; };
F6BCCFE21C8380A400FE31AE /* lib in Resources */ = {isa = PBXBuildFile; fileRef = F6BCCFDF1C83809A00FE31AE /* lib */; };
F6CB31001C8EDDAB0070EF3F /* js_collection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */; };
F6CB31011C8EDDBB0070EF3F /* js_collection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -100,24 +112,19 @@
/* Begin PBXFileReference section */
02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = "<group>"; };
025678951CAB392000FB8501 /* jsc_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_types.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>"; };
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>"; };
029048011C0428DF00ABDED4 /* jsc_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_init.cpp; sourceTree = "<group>"; };
029048021C0428DF00ABDED4 /* jsc_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsc_init.h; 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>"; };
029048061C0428DF00ABDED4 /* js_realm_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm_object.hpp; sourceTree = "<group>"; };
029048071C0428DF00ABDED4 /* js_realm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_realm.cpp; sourceTree = "<group>"; };
029048081C0428DF00ABDED4 /* js_realm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_realm.hpp; sourceTree = "<group>"; };
029048091C0428DF00ABDED4 /* js_results.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_results.cpp; sourceTree = "<group>"; };
0290480A1C0428DF00ABDED4 /* js_results.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_results.hpp; sourceTree = "<group>"; };
0290480B1C0428DF00ABDED4 /* js_schema.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_schema.cpp; sourceTree = "<group>"; };
0290480C1C0428DF00ABDED4 /* js_schema.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_schema.hpp; sourceTree = "<group>"; };
0290480D1C0428DF00ABDED4 /* js_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_util.cpp; sourceTree = "<group>"; };
0290480E1C0428DF00ABDED4 /* js_util.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = "<group>"; };
0290480F1C0428DF00ABDED4 /* rpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rpc.cpp; sourceTree = "<group>"; };
029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = "<group>"; };
029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = "<group>"; };
@ -161,7 +168,37 @@
02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = transact_log_handler.hpp; path = src/impl/transact_log_handler.hpp; sourceTree = "<group>"; };
02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier_base.hpp; path = src/impl/weak_realm_notifier_base.hpp; sourceTree = "<group>"; };
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier.hpp; path = src/impl/weak_realm_notifier.hpp; sourceTree = "<group>"; };
F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = "<group>"; };
F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = "<group>"; };
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = "<group>"; };
F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = "<group>"; };
F60102F71CBDA6D400EC01BA /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = "<group>"; };
F60103071CC4B3DF00EC01BA /* node_protected.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_protected.hpp; sourceTree = "<group>"; };
F60103081CC4B4F900EC01BA /* jsc_protected.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_protected.hpp; sourceTree = "<group>"; };
F60103091CC4B5E800EC01BA /* jsc_context.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_context.hpp; sourceTree = "<group>"; };
F601030A1CC4B64E00EC01BA /* node_context.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_context.hpp; sourceTree = "<group>"; };
F601030B1CC4B6C900EC01BA /* jsc_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_value.hpp; sourceTree = "<group>"; };
F601030C1CC4B72B00EC01BA /* node_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_value.hpp; sourceTree = "<group>"; };
F601030D1CC4B76F00EC01BA /* jsc_object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_object.hpp; sourceTree = "<group>"; };
F601030E1CC4B7C900EC01BA /* node_object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_object.hpp; sourceTree = "<group>"; };
F601030F1CC4B80800EC01BA /* jsc_function.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_function.hpp; sourceTree = "<group>"; };
F60103101CC4B86000EC01BA /* node_function.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_function.hpp; sourceTree = "<group>"; };
F60103111CC4BA6500EC01BA /* jsc_exception.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_exception.hpp; sourceTree = "<group>"; };
F60103121CC4CBF000EC01BA /* node_exception.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_exception.hpp; sourceTree = "<group>"; };
F60103131CC4CC4500EC01BA /* jsc_string.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_string.hpp; sourceTree = "<group>"; };
F60103141CC4CC8C00EC01BA /* jsc_return_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_return_value.hpp; sourceTree = "<group>"; };
F60103151CC4CCFD00EC01BA /* node_return_value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_return_value.hpp; sourceTree = "<group>"; };
F60103161CC4CD2F00EC01BA /* node_string.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_string.hpp; sourceTree = "<group>"; };
F61378781C18EAAC008BFC51 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = "<group>"; };
F620F0521CAF0B600082977B /* js_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_class.hpp; sourceTree = "<group>"; };
F620F0531CAF2EF70082977B /* jsc_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsc_class.hpp; sourceTree = "<group>"; };
F620F0551CB655A50082977B /* node_class.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_class.hpp; sourceTree = "<group>"; };
F620F0571CB766DA0082977B /* node_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_init.cpp; sourceTree = "<group>"; };
F620F0591CB7B4C80082977B /* js_object_accessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_object_accessor.hpp; sourceTree = "<group>"; };
F620F0741CB9F60C0082977B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; };
F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = "<group>"; };
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_dummy.cpp; sourceTree = "<group>"; };
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; };
F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RealmJS.mm; sourceTree = "<group>"; };
F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libGCDWebServers.a; sourceTree = BUILT_PRODUCTS_DIR; };
@ -198,14 +235,14 @@
F63FF32C1C16432E00B3B8E0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
F63FF32E1C16433900B3B8E0 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
F63FF3301C16434400B3B8E0 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
F6874A351CAC792D00EEEE36 /* node_types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_types.hpp; sourceTree = "<group>"; };
F6874A3E1CACA5A900EEEE36 /* js_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_types.hpp; sourceTree = "<group>"; };
F68A278A1BC2722A0063D40A /* RJSModuleLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RJSModuleLoader.h; path = ios/RJSModuleLoader.h; sourceTree = "<group>"; };
F68A278B1BC2722A0063D40A /* RJSModuleLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RJSModuleLoader.m; path = ios/RJSModuleLoader.m; sourceTree = "<group>"; };
F6BB7DEF1BF681BC00D0A69E /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = "<group>"; };
F6BB7DF01BF681BC00D0A69E /* base64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = "<group>"; };
F6BCCFDF1C83809A00FE31AE /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = ../../lib; sourceTree = SOURCE_ROOT; };
F6C3FBBC1BF680EC00E6FFD4 /* json.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = "<group>"; };
F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = js_collection.cpp; sourceTree = "<group>"; };
F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_collection.hpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -227,6 +264,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
F62BF8F81CAC71780022BCDC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F63FF2ED1C16405C00B3B8E0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -242,23 +287,17 @@
children = (
F6BCCFDF1C83809A00FE31AE /* lib */,
F62A35131C18E6E2004A917D /* iOS */,
F60103051CC4ADE500EC01BA /* JS */,
F6874A441CAD2ACD00EEEE36 /* JSC */,
F62BF9001CAC72C40022BCDC /* Node */,
F62A35141C18E783004A917D /* Object Store */,
029048011C0428DF00ABDED4 /* js_init.cpp */,
029048021C0428DF00ABDED4 /* js_init.h */,
F6CB30FC1C8EDD760070EF3F /* js_collection.cpp */,
F6CB30FD1C8EDD760070EF3F /* js_collection.hpp */,
029048031C0428DF00ABDED4 /* js_list.cpp */,
F60102F71CBDA6D400EC01BA /* js_collection.hpp */,
029048041C0428DF00ABDED4 /* js_list.hpp */,
029048051C0428DF00ABDED4 /* js_object.cpp */,
029048061C0428DF00ABDED4 /* js_object.hpp */,
029048061C0428DF00ABDED4 /* js_realm_object.hpp */,
029048071C0428DF00ABDED4 /* js_realm.cpp */,
029048081C0428DF00ABDED4 /* js_realm.hpp */,
029048091C0428DF00ABDED4 /* js_results.cpp */,
0290480A1C0428DF00ABDED4 /* js_results.hpp */,
0290480B1C0428DF00ABDED4 /* js_schema.cpp */,
0290480C1C0428DF00ABDED4 /* js_schema.hpp */,
0290480D1C0428DF00ABDED4 /* js_util.cpp */,
0290480E1C0428DF00ABDED4 /* js_util.hpp */,
029048351C042A3C00ABDED4 /* platform.hpp */,
0290480F1C0428DF00ABDED4 /* rpc.cpp */,
029048101C0428DF00ABDED4 /* rpc.hpp */,
@ -285,6 +324,7 @@
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */,
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */,
F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */,
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */,
);
name = Products;
sourceTree = "<group>";
@ -307,6 +347,7 @@
02B58CCF1AE99D8C009B348C /* Frameworks */ = {
isa = PBXGroup;
children = (
F620F0741CB9F60C0082977B /* CoreFoundation.framework */,
02A3C7A41BC4341500B1A7BE /* libc++.tbd */,
F63FF3301C16434400B3B8E0 /* libz.tbd */,
F63FF32E1C16433900B3B8E0 /* libxml2.tbd */,
@ -318,6 +359,17 @@
name = Frameworks;
sourceTree = "<group>";
};
F60103051CC4ADE500EC01BA /* JS */ = {
isa = PBXGroup;
children = (
F620F0521CAF0B600082977B /* js_class.hpp */,
F6874A3E1CACA5A900EEEE36 /* js_types.hpp */,
F6267BC91CADC30000AC36B1 /* js_util.hpp */,
F620F0591CB7B4C80082977B /* js_object_accessor.hpp */,
);
name = JS;
sourceTree = "<group>";
};
F62A35131C18E6E2004A917D /* iOS */ = {
isa = PBXGroup;
children = (
@ -372,6 +424,28 @@
path = "object-store";
sourceTree = "<group>";
};
F62BF9001CAC72C40022BCDC /* Node */ = {
isa = PBXGroup;
children = (
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */,
F60102CF1CBB814A00EC01BA /* node_init.hpp */,
F620F0571CB766DA0082977B /* node_init.cpp */,
F620F0551CB655A50082977B /* node_class.hpp */,
F6874A351CAC792D00EEEE36 /* node_types.hpp */,
F60103161CC4CD2F00EC01BA /* node_string.hpp */,
F601030A1CC4B64E00EC01BA /* node_context.hpp */,
F601030C1CC4B72B00EC01BA /* node_value.hpp */,
F601030E1CC4B7C900EC01BA /* node_object.hpp */,
F60103101CC4B86000EC01BA /* node_function.hpp */,
F60103121CC4CBF000EC01BA /* node_exception.hpp */,
F60103071CC4B3DF00EC01BA /* node_protected.hpp */,
F60103151CC4CCFD00EC01BA /* node_return_value.hpp */,
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */,
);
name = Node;
path = node;
sourceTree = "<group>";
};
F63FF2FB1C1642BB00B3B8E0 /* GCDWebServer */ = {
isa = PBXGroup;
children = (
@ -432,6 +506,28 @@
path = Responses;
sourceTree = "<group>";
};
F6874A441CAD2ACD00EEEE36 /* JSC */ = {
isa = PBXGroup;
children = (
029048021C0428DF00ABDED4 /* jsc_init.h */,
F60102D11CBB865A00EC01BA /* jsc_init.hpp */,
029048011C0428DF00ABDED4 /* jsc_init.cpp */,
F620F0531CAF2EF70082977B /* jsc_class.hpp */,
025678951CAB392000FB8501 /* jsc_types.hpp */,
F60103131CC4CC4500EC01BA /* jsc_string.hpp */,
F60103091CC4B5E800EC01BA /* jsc_context.hpp */,
F601030B1CC4B6C900EC01BA /* jsc_value.hpp */,
F601030D1CC4B76F00EC01BA /* jsc_object.hpp */,
F601030F1CC4B80800EC01BA /* jsc_function.hpp */,
F60103111CC4BA6500EC01BA /* jsc_exception.hpp */,
F60103081CC4B4F900EC01BA /* jsc_protected.hpp */,
F60103141CC4CC8C00EC01BA /* jsc_return_value.hpp */,
F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */,
);
name = JSC;
path = jsc;
sourceTree = "<group>";
};
F6C3FBB41BF680D000E6FFD4 /* Vendor */ = {
isa = PBXGroup;
children = (
@ -452,20 +548,26 @@
buildActionMask = 2147483647;
files = (
0290483B1C042EE200ABDED4 /* RealmJS.h in Headers */,
029048121C0428DF00ABDED4 /* js_init.h in Headers */,
029048121C0428DF00ABDED4 /* jsc_init.h in Headers */,
029048201C0428DF00ABDED4 /* rpc.hpp in Headers */,
0290481E1C0428DF00ABDED4 /* js_util.hpp in Headers */,
0290481A1C0428DF00ABDED4 /* js_results.hpp in Headers */,
029048181C0428DF00ABDED4 /* js_realm.hpp in Headers */,
029048141C0428DF00ABDED4 /* js_list.hpp in Headers */,
F6BB7DF21BF681BC00D0A69E /* base64.hpp in Headers */,
F6CB31011C8EDDBB0070EF3F /* js_collection.hpp in Headers */,
0290481C1C0428DF00ABDED4 /* js_schema.hpp in Headers */,
029048161C0428DF00ABDED4 /* js_object.hpp in Headers */,
029048161C0428DF00ABDED4 /* js_realm_object.hpp in Headers */,
029048371C042A3C00ABDED4 /* platform.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F62BF8F91CAC71780022BCDC /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
@ -507,6 +609,23 @@
productReference = 02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
F62BF8FA1CAC71780022BCDC /* RealmNode */ = {
isa = PBXNativeTarget;
buildConfigurationList = F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */;
buildPhases = (
F62BF8F71CAC71780022BCDC /* Sources */,
F62BF8F81CAC71780022BCDC /* Frameworks */,
F62BF8F91CAC71780022BCDC /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = RealmNode;
productName = RealmNode;
productReference = F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
F63FF2B01C1241E500B3B8E0 /* RealmJS static */ = {
isa = PBXNativeTarget;
buildConfigurationList = F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS static" */;
@ -555,6 +674,9 @@
02B58CBB1AE99CEC009B348C = {
CreatedOnToolsVersion = 6.3.1;
};
F62BF8FA1CAC71780022BCDC = {
CreatedOnToolsVersion = 7.3;
};
F63FF2B01C1241E500B3B8E0 = {
CreatedOnToolsVersion = 7.1.1;
};
@ -577,6 +699,7 @@
targets = (
F63FF2EF1C16405C00B3B8E0 /* GCDWebServers */,
F63FF2B01C1241E500B3B8E0 /* RealmJS static */,
F62BF8FA1CAC71780022BCDC /* RealmNode */,
02B58CB01AE99CEC009B348C /* RealmJS */,
02B58CBB1AE99CEC009B348C /* RealmJSTests */,
);
@ -615,7 +738,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cp ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"";
shellScript = "DEST=\"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"\nrm -f \"$DEST\"\ncp ../object-store/tests/query.json \"$DEST\"";
};
F63FF2C51C12462600B3B8E0 /* Download Core */ = {
isa = PBXShellScriptBuildPhase;
@ -644,28 +767,47 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
F62BF8F71CAC71780022BCDC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */,
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */,
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */,
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */,
F60102D51CBB96AE00EC01BA /* list.cpp in Sources */,
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */,
F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */,
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */,
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */,
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */,
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */,
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */,
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */,
F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */,
F620F0581CB766DA0082977B /* node_init.cpp in Sources */,
F60102D91CBB96C100EC01BA /* schema.cpp in Sources */,
F60102E91CBCAEC500EC01BA /* platform.mm in Sources */,
F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F63FF2AD1C1241E500B3B8E0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F6CB31001C8EDDAB0070EF3F /* js_collection.cpp in Sources */,
02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */,
F63FF2E81C159C4B00B3B8E0 /* platform.mm in Sources */,
02F59EC31C88F17D007F774C /* results.cpp in Sources */,
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
F63FF2C61C12469E00B3B8E0 /* js_init.cpp in Sources */,
F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */,
02F59ECA1C88F190007F774C /* parser.cpp in Sources */,
F63FF2C71C12469E00B3B8E0 /* js_list.cpp in Sources */,
F63FF2C81C12469E00B3B8E0 /* js_object.cpp in Sources */,
02F59EE11C88F2BB007F774C /* async_query.cpp in Sources */,
02F59EC01C88F17D007F774C /* list.cpp in Sources */,
02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */,
02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */,
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */,
F63FF2CA1C12469E00B3B8E0 /* js_results.cpp in Sources */,
02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */,
F63FF2CB1C12469E00B3B8E0 /* js_schema.cpp in Sources */,
F63FF2CC1C12469E00B3B8E0 /* js_util.cpp in Sources */,
02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */,
02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */,
02F59EC41C88F17D007F774C /* schema.cpp in Sources */,
@ -759,6 +901,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = ../../core;
MTL_ENABLE_DEBUG_INFO = YES;
@ -767,6 +914,7 @@
"$(OTHER_CFLAGS)",
"-isystem",
../../core/include,
"-ftemplate-backtrace-limit=0",
);
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@ -811,6 +959,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = ../../core;
MTL_ENABLE_DEBUG_INFO = NO;
@ -818,6 +971,7 @@
"$(OTHER_CFLAGS)",
"-isystem",
../../core/include,
"-ftemplate-backtrace-limit=0",
);
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@ -946,6 +1100,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = ../../core;
MTL_ENABLE_DEBUG_INFO = YES;
@ -954,6 +1113,7 @@
"$(OTHER_CFLAGS)",
"-isystem",
../../core/include,
"-ftemplate-backtrace-limit=0",
);
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@ -1004,6 +1164,83 @@
};
name = GCov_Build;
};
F62BF8FC1CAC71780022BCDC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../node_modules/nan",
/usr/local/include/node,
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
/usr/local/lib,
);
OTHER_LDFLAGS = (
"-lrealm",
"-lv8",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Debug;
};
F62BF8FD1CAC71780022BCDC /* GCov_Build */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../node_modules/nan",
/usr/local/include/node,
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
/usr/local/lib,
);
OTHER_LDFLAGS = (
"-lrealm",
"-lv8",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = GCov_Build;
};
F62BF8FE1CAC71780022BCDC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../node_modules/nan",
/usr/local/include/node,
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
/usr/local/lib,
);
OTHER_LDFLAGS = (
"-lrealm",
"-lv8",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
name = Release;
};
F63FF2B71C1241E500B3B8E0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -1012,12 +1249,6 @@
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1026,12 +1257,6 @@
F63FF2B81C1241E500B3B8E0 /* GCov_Build */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1040,12 +1265,6 @@
F63FF2B91C1241E500B3B8E0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../object-store/external/pegtl",
"$(SRCROOT)/../object-store/src",
"$(SRCROOT)/../../vendor",
);
PRODUCT_NAME = RealmJS;
SKIP_INSTALL = YES;
};
@ -1059,6 +1278,7 @@
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1068,6 +1288,7 @@
F63FF2F71C16405D00B3B8E0 /* GCov_Build */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1077,6 +1298,7 @@
F63FF2F81C16405D00B3B8E0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1116,6 +1338,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F62BF8FF1CAC71780022BCDC /* Build configuration list for PBXNativeTarget "RealmNode" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F62BF8FC1CAC71780022BCDC /* Debug */,
F62BF8FD1CAC71780022BCDC /* GCov_Build */,
F62BF8FE1CAC71780022BCDC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F63FF2C41C1241E500B3B8E0 /* Build configuration list for PBXNativeTarget "RealmJS static" */ = {
isa = XCConfigurationList;
buildConfigurations = (

93
src/js_class.hpp Normal file
View File

@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////
//
// 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 <map>
#include <string>
#include <vector>
#include "js_types.hpp"
namespace realm {
namespace js {
template<typename T>
using ConstructorType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[]);
template<typename T>
using MethodType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[], ReturnValue<T> &);
template<typename T>
struct PropertyType {
using GetterType = void(typename T::Context, typename T::Object, ReturnValue<T> &);
using SetterType = void(typename T::Context, typename T::Object, typename T::Value);
typename T::PropertyGetterCallback getter;
typename T::PropertySetterCallback setter;
};
template<typename T>
struct IndexPropertyType {
using GetterType = void(typename T::Context, typename T::Object, uint32_t, ReturnValue<T> &);
using SetterType = bool(typename T::Context, typename T::Object, uint32_t, typename T::Value);
typename T::IndexPropertyGetterCallback getter;
typename T::IndexPropertySetterCallback setter;
};
template<typename T>
struct StringPropertyType {
using GetterType = void(typename T::Context, typename T::Object, const String<T> &, ReturnValue<T> &);
using SetterType = bool(typename T::Context, typename T::Object, const String<T> &, typename T::Value);
using EnumeratorType = std::vector<String<T>>(typename T::Context, typename T::Object);
typename T::StringPropertyGetterCallback getter;
typename T::StringPropertySetterCallback setter;
typename T::StringPropertyEnumeratorCallback enumerator;
};
template<typename T>
using MethodMap = std::map<std::string, typename T::FunctionCallback>;
template<typename T>
using PropertyMap = std::map<std::string, PropertyType<T>>;
template<typename T, typename U, typename V = void>
struct ClassDefinition {
using Internal = U;
using Parent = V;
// Every subclass *must* at least have a name.
// std::string const name;
// ClassDefinition specializations should inherit from this class and override what's needed below.
ConstructorType<T>* const constructor = nullptr;
MethodMap<T> const static_methods = {};
PropertyMap<T> const static_properties = {};
MethodMap<T> const methods = {};
PropertyMap<T> const properties = {};
IndexPropertyType<T> const index_accessor = {};
StringPropertyType<T> const string_accessor = {};
};
template<typename T, typename ClassType>
class ObjectWrap;
} // js
} // realm

View File

@ -18,6 +18,18 @@
#pragma once
#include "js_util.hpp"
#include "js_class.hpp"
JSClassRef RJSCollectionClass();
namespace realm {
namespace js {
// Empty class that merely serves as useful type for now.
class Collection {};
template<typename T>
struct CollectionClass : ClassDefinition<T, Collection> {
std::string const name = "Collection";
};
} // js
} // realm

View File

@ -1,117 +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_init.h"
#include "js_realm.hpp"
#include "js_object.hpp"
#include "js_collection.hpp"
#include "js_list.hpp"
#include "js_results.hpp"
#include "js_util.hpp"
#include "js_schema.hpp"
#include "platform.hpp"
#include "shared_realm.hpp"
#include "impl/realm_coordinator.hpp"
#include <algorithm>
#include <cassert>
extern "C" {
JSValueRef RJSTypeGet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
std::string str = RJSStringForJSString(propertyName);
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return RJSValueForString(ctx, str);
}
JSClassRef RJSRealmTypeClass() {
JSClassDefinition realmTypesDefinition = kJSClassDefinitionEmpty;
realmTypesDefinition.className = "PropTypes";
JSStaticValue types[] = {
{ "BOOL", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "INT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "FLOAT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DOUBLE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "STRING", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DATE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DATA", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "OBJECT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ NULL, NULL, NULL, 0 }
};
realmTypesDefinition.staticValues = types;
return JSClassCreate(&realmTypesDefinition);
}
static JSObjectRef UncallableConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
*exception = RJSMakeError(ctx, "Illegal constructor");
return NULL;
}
static JSValueRef ClearTestState(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
RJSClearTestState();
return NULL;
}
JSObjectRef RJSConstructorCreate(JSContextRef ctx) {
static JSStringRef clearTestStateString = JSStringCreateWithUTF8CString("clearTestState");
static JSStringRef collectionString = JSStringCreateWithUTF8CString("Collection");
static JSStringRef listString = JSStringCreateWithUTF8CString("List");
static JSStringRef resultsString = JSStringCreateWithUTF8CString("Results");
static JSStringRef typeString = JSStringCreateWithUTF8CString("Types");
JSObjectRef realmObject = JSObjectMake(ctx, RJSRealmConstructorClass(), NULL);
JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
JSObjectRef collectionConstructor = JSObjectMakeConstructor(ctx, RJSCollectionClass(), UncallableConstructor);
RJSValidatedSetProperty(ctx, realmObject, collectionString, collectionConstructor, attributes);
JSObjectRef listConstructor = JSObjectMakeConstructor(ctx, RJSListClass(), UncallableConstructor);
RJSValidatedSetProperty(ctx, realmObject, listString, listConstructor, attributes);
JSObjectRef resultsContructor = JSObjectMakeConstructor(ctx, RJSResultsClass(), UncallableConstructor);
RJSValidatedSetProperty(ctx, realmObject, resultsString, resultsContructor, attributes);
JSObjectRef typesObject = JSObjectMake(ctx, RJSRealmTypeClass(), NULL);
RJSValidatedSetProperty(ctx, realmObject, typeString, typesObject, attributes);
JSObjectRef clearTestStateFunction = JSObjectMakeFunctionWithCallback(ctx, clearTestStateString, ClearTestState);
RJSValidatedSetProperty(ctx, realmObject, clearTestStateString, clearTestStateFunction, attributes);
return realmObject;
}
void RJSInitializeInContext(JSContextRef ctx) {
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
JSObjectRef realmObject = RJSConstructorCreate(ctx);
JSValueRef exception = NULL;
JSStringRef nameString = JSStringCreateWithUTF8CString("Realm");
JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
JSObjectSetProperty(ctx, globalObject, nameString, realmObject, attributes, &exception);
JSStringRelease(nameString);
assert(!exception);
}
void RJSClearTestState() {
realm::_impl::RealmCoordinator::clear_all_caches();
realm::remove_realm_files_from_directory(realm::default_realm_file_directory());
}
}

View File

@ -1,277 +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;
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);
}
}
JSValueRef ListPush(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
for (size_t i = 0; i < argumentCount; i++) {
list->add(ctx, arguments[i]);
}
return JSValueMakeNumber(ctx, list->size());
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListPop(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
size_t size = list->size();
if (size == 0) {
list->verify_in_transaction();
return JSValueMakeUndefined(ctx);
}
size_t index = size - 1;
JSValueRef obj = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(index)));
list->remove(index);
return obj;
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListUnshift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
for (size_t i = 0; i < argumentCount; i++) {
list->insert(ctx, arguments[i], i);
}
return JSValueMakeNumber(ctx, list->size());
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListShift(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
if (list->size() == 0) {
list->verify_in_transaction();
return JSValueMakeUndefined(ctx);
}
JSValueRef obj = RJSObjectCreate(ctx, Object(list->get_realm(), list->get_object_schema(), list->get(0)));
list->remove(0);
return obj;
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListSplice(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
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<JSObjectRef> 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);
}
return JSObjectMakeArray(ctx, remove, removedObjects.data(), jsException);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListStaticResults(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
return RJSResultsCreate(ctx, list->get_realm(), list->get_object_schema(), std::move(list->get_query()), false);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListFiltered(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
return RJSResultsCreateFiltered(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ListSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
List *list = RJSGetInternal<List *>(thisObject);
RJSValidateArgumentRange(argumentCount, 1, 2);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
return RJSResultsCreateSorted(ctx, sharedRealm, list->get_object_schema(), std::move(list->get_query()), argumentCount, arguments);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
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,217 @@
#pragma once
#include "js_collection.hpp"
#include "js_realm_object.hpp"
#include "js_results.hpp"
#include "js_types.hpp"
#include "js_util.hpp"
#include "shared_realm.hpp"
#include "list.hpp"
#include "parser.hpp"
#include "query_builder.hpp"
JSClassRef RJSListClass();
JSObjectRef RJSListCreate(JSContextRef ctx, realm::List &list);
namespace realm {
namespace js {
template<typename T>
class List {
using ContextType = typename T::Context;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ReturnValue = js::ReturnValue<T>;
public:
static ObjectType create_instance(ContextType, realm::List &);
// properties
static void get_length(ContextType, ObjectType, ReturnValue &);
static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &);
static bool set_index(ContextType, ObjectType, uint32_t, ValueType);
// methods
static void push(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void pop(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void unshift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void shift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void splice(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
};
template<typename T>
struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
using List = js::List<T>;
std::string const name = "List";
MethodMap<T> const methods = {
{"push", wrap<List::push>},
{"pop", wrap<List::pop>},
{"unshift", wrap<List::unshift>},
{"shift", wrap<List::shift>},
{"splice", wrap<List::splice>},
{"snapshot", wrap<List::snapshot>},
{"filtered", wrap<List::filtered>},
{"sorted", wrap<List::sorted>},
};
PropertyMap<T> const properties = {
{"length", {wrap<List::get_length>, nullptr}},
};
IndexPropertyType<T> const index_accessor = {wrap<List::get_index>, wrap<List::set_index>};
};
template<typename T>
typename T::Object List<T>::create_instance(ContextType ctx, realm::List &list) {
return create_object<T, ListClass<T>>(ctx, new realm::List(list));
}
template<typename T>
void List<T>::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) {
auto list = get_internal<T, ListClass<T>>(object);
return_value.set((uint32_t)list->size());
}
template<typename T>
void List<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
auto list = get_internal<T, ListClass<T>>(object);
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
return_value.set(RealmObject<T>::create_instance(ctx, realm_object));
}
template<typename T>
bool List<T>::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) {
auto list = get_internal<T, ListClass<T>>(object);
list->set(ctx, value, index);
return true;
}
template<typename T>
void List<T>::push(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
for (size_t i = 0; i < argc; i++) {
list->add(ctx, arguments[i]);
}
return_value.set((uint32_t)list->size());
}
template<typename T>
void List<T>::pop(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
size_t size = list->size();
if (size == 0) {
list->verify_in_transaction();
return_value.set_undefined();
}
else {
size_t index = size - 1;
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
return_value.set(RealmObject<T>::create_instance(ctx, realm_object));
list->remove(index);
}
}
template<typename T>
void List<T>::unshift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
for (size_t i = 0; i < argc; i++) {
list->insert(ctx, arguments[i], i);
}
return_value.set((uint32_t)list->size());
}
template<typename T>
void List<T>::shift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
if (list->size() == 0) {
list->verify_in_transaction();
return_value.set_undefined();
}
else {
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(0));
return_value.set(RealmObject<T>::create_instance(ctx, realm_object));
list->remove(0);
}
}
template<typename T>
void List<T>::splice(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
size_t size = list->size();
long index = std::min<long>(Value::to_number(ctx, arguments[0]), size);
if (index < 0) {
index = std::max<long>(size + index, 0);
}
size_t remove;
if (argc < 2) {
remove = size - index;
}
else {
remove = std::max<long>(Value::to_number(ctx, arguments[1]), 0);
remove = std::min<long>(remove, size - index);
}
std::vector<ValueType> removed_objects;
removed_objects.reserve(remove);
for (size_t i = 0; i < remove; i++) {
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
removed_objects.push_back(RealmObject<T>::create_instance(ctx, realm_object));
list->remove(index);
}
for (size_t i = 2; i < argc; i++) {
list->insert(ctx, arguments[i], index + i - 2);
}
return_value.set(Object::create_array(ctx, removed_objects));
}
template<typename T>
void List<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
return_value.set(Results<T>::create_instance(ctx, *list, false));
}
template<typename T>
void List<T>::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
return_value.set(Results<T>::create_filtered(ctx, *list, argc, arguments));
}
template<typename T>
void List<T>::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
auto list = get_internal<T, ListClass<T>>(this_object);
return_value.set(Results<T>::create_sorted(ctx, *list, argc, arguments));
}
} // js
} // realm

View File

@ -1,290 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#include "js_util.hpp"
#include "js_object.hpp"
#include "js_results.hpp"
#include "js_schema.hpp"
#include "js_list.hpp"
#include "js_realm.hpp"
#include "object_store.hpp"
#include "object_accessor.hpp"
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm;
JSValueRef ObjectGetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef* exception) {
try {
Object *obj = RJSGetInternal<Object *>(jsObject);
return obj->get_property_value<JSValueRef>(ctx, RJSStringForJSString(jsPropertyName));
} catch (InvalidPropertyException &ex) {
// getters for nonexistent properties in JS should always return undefined
} catch (std::exception &ex) {
if (exception) {
*exception = RJSMakeError(ctx, ex);
}
}
return NULL;
}
bool ObjectSetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef value, JSValueRef* exception) {
try {
Object *obj = RJSGetInternal<Object *>(jsObject);
obj->set_property_value(ctx, RJSStringForJSString(jsPropertyName), value, true);
} catch (std::exception &ex) {
if (exception) {
*exception = RJSMakeError(ctx, ex);
}
return false;
}
return true;
}
void ObjectPropertyNames(JSContextRef ctx, JSObjectRef jsObject, JSPropertyNameAccumulatorRef propertyNames) {
Object *obj = RJSGetInternal<Object *>(jsObject);
for (auto &prop : obj->get_object_schema().properties) {
JSStringRef propertyName = RJSStringForString(prop.name);
JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
JSStringRelease(propertyName);
}
}
JSClassRef RJSObjectClass() {
static JSClassRef s_objectClass = RJSCreateWrapperClass<Object *>("RealmObject", ObjectGetProperty, ObjectSetProperty, NULL, ObjectPropertyNames);
return s_objectClass;
}
JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) {
static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype");
JSObjectRef constructor = RJSConstructors(object.realm().get())[object.get_object_schema().name];
JSObjectRef prototype = constructor ? RJSValidatedObjectProperty(ctx, constructor, prototypeString) : NULL;
JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype);
if (constructor) {
JSValueRef exception = NULL;
JSValueRef result = JSObjectCallAsFunction(ctx, constructor, jsObject, 0, NULL, &exception);
if (exception) {
throw RJSException(ctx, exception);
}
else if (result != jsObject && !JSValueIsNull(ctx, result) && !JSValueIsUndefined(ctx, result)) {
throw std::runtime_error("Realm object constructor must not return another value");
}
}
return jsObject;
}
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) {
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
JSStringRef propStr = RJSStringForString(prop_name);
bool ret = JSObjectHasProperty(ctx, object, propStr);
JSStringRelease(propStr);
return ret;
}
template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) {
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
JSStringRef propStr = RJSStringForString(prop_name);
JSValueRef ex = NULL;
JSValueRef ret = JSObjectGetProperty(ctx, object, propStr, &ex);
if (ex) {
throw RJSException(ctx, ex);
}
JSStringRelease(propStr);
return ret;
}
template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
return defaults.find(prop_name) != defaults.end();
}
template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
return defaults[prop_name];
}
template<> bool RJSAccessor::is_null(JSContextRef ctx, JSValueRef &val) {
return JSValueIsNull(ctx, val) || JSValueIsUndefined(ctx, val);
}
template<> JSValueRef RJSAccessor::null_value(JSContextRef ctx) {
return JSValueMakeNull(ctx);
}
template<> bool RJSAccessor::to_bool(JSContextRef ctx, JSValueRef &val) {
if (!JSValueIsBoolean(ctx, val)) {
throw std::runtime_error("Property expected to be of type boolean");
}
return JSValueToBoolean(ctx, val);
}
template<> JSValueRef RJSAccessor::from_bool(JSContextRef ctx, bool b) {
return JSValueMakeBoolean(ctx, b);
}
template<> long long RJSAccessor::to_long(JSContextRef ctx, JSValueRef &val) {
return RJSValidatedValueToNumber(ctx, val);
}
template<> JSValueRef RJSAccessor::from_long(JSContextRef ctx, long long l) {
return JSValueMakeNumber(ctx, l);
}
template<> float RJSAccessor::to_float(JSContextRef ctx, JSValueRef &val) {
return RJSValidatedValueToNumber(ctx, val);
}
template<> JSValueRef RJSAccessor::from_float(JSContextRef ctx, float f) {
return JSValueMakeNumber(ctx, f);
}
template<> double RJSAccessor::to_double(JSContextRef ctx, JSValueRef &val) {
return RJSValidatedValueToNumber(ctx, val);
}
template<> JSValueRef RJSAccessor::from_double(JSContextRef ctx, double d) {
return JSValueMakeNumber(ctx, d);
}
template<> std::string RJSAccessor::to_string(JSContextRef ctx, JSValueRef &val) {
return RJSValidatedStringForValue(ctx, val);
}
template<> JSValueRef RJSAccessor::from_string(JSContextRef ctx, StringData s) {
return RJSValueForString(ctx, s);
}
template<> std::string RJSAccessor::to_binary(JSContextRef ctx, JSValueRef &val) {
static JSStringRef arrayBufferString = JSStringCreateWithUTF8CString("ArrayBuffer");
static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer");
static JSStringRef byteLengthString = JSStringCreateWithUTF8CString("byteLength");
static JSStringRef byteOffsetString = JSStringCreateWithUTF8CString("byteOffset");
static JSStringRef isViewString = JSStringCreateWithUTF8CString("isView");
static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array");
JSObjectRef arrayBufferConstructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), arrayBufferString);
JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString);
JSValueRef uint8ArrayArguments[3];
size_t uint8ArrayArgumentsCount = 0;
// Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView).
if (JSValueIsInstanceOfConstructor(ctx, val, arrayBufferConstructor, NULL)) {
uint8ArrayArguments[0] = val;
uint8ArrayArgumentsCount = 1;
}
else if (JSObjectRef object = JSValueToObject(ctx, val, NULL)) {
// Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val).
JSObjectRef isViewMethod = RJSValidatedObjectProperty(ctx, arrayBufferConstructor, isViewString);
JSValueRef isView = JSObjectCallAsFunction(ctx, isViewMethod, arrayBufferConstructor, 1, &val, NULL);
if (isView && JSValueToBoolean(ctx, isView)) {
uint8ArrayArguments[0] = RJSValidatedObjectProperty(ctx, object, bufferString);
uint8ArrayArguments[1] = RJSValidatedPropertyValue(ctx, object, byteOffsetString);
uint8ArrayArguments[2] = RJSValidatedPropertyValue(ctx, object, byteLengthString);
uint8ArrayArgumentsCount = 3;
}
}
if (!uint8ArrayArgumentsCount) {
throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary");
}
JSValueRef exception = NULL;
JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, uint8ArrayArgumentsCount, uint8ArrayArguments, &exception);
if (exception) {
throw RJSException(ctx, exception);
}
size_t byteCount = RJSValidatedListLength(ctx, uint8Array);
std::string bytes(byteCount, 0);
for (size_t i = 0; i < byteCount; i++) {
JSValueRef byteValue = JSObjectGetPropertyAtIndex(ctx, uint8Array, (unsigned)i, NULL);
bytes[i] = JSValueToNumber(ctx, byteValue, NULL);
}
return bytes;
}
template<> JSValueRef RJSAccessor::from_binary(JSContextRef ctx, BinaryData data) {
static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer");
static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array");
size_t byteCount = data.size();
JSValueRef byteCountValue = JSValueMakeNumber(ctx, byteCount);
JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString);
JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, 1, &byteCountValue, NULL);
for (size_t i = 0; i < byteCount; i++) {
JSValueRef num = JSValueMakeNumber(ctx, data[i]);
JSObjectSetPropertyAtIndex(ctx, uint8Array, (unsigned)i, num, NULL);
}
return RJSValidatedObjectProperty(ctx, uint8Array, bufferString);
}
template<> DateTime RJSAccessor::to_datetime(JSContextRef ctx, JSValueRef &val) {
JSObjectRef object = RJSValidatedValueToDate(ctx, val);
double utc = RJSValidatedValueToNumber(ctx, object);
return DateTime(utc);
}
template<> JSValueRef RJSAccessor::from_datetime(JSContextRef ctx, DateTime dt) {
JSValueRef time = JSValueMakeNumber(ctx, dt.get_datetime());
return JSObjectMakeDate(ctx, 1, &time, NULL);
}
template<> size_t RJSAccessor::to_existing_object_index(JSContextRef ctx, JSValueRef &val) {
JSObjectRef object = RJSValidatedValueToObject(ctx, val);
if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) {
return RJSGetInternal<Object *>(object)->row().get_index();
}
throw std::runtime_error("object is not a Realm Object");
}
template<> size_t RJSAccessor::to_object_index(JSContextRef ctx, SharedRealm realm, JSValueRef &val, const std::string &type, bool try_update) {
JSObjectRef object = RJSValidatedValueToObject(ctx, val);
if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) {
return RJSGetInternal<Object *>(object)->row().get_index();
}
auto object_schema = realm->config().schema->find(type);
if (RJSIsValueArray(ctx, object)) {
object = RJSDictForPropertyArray(ctx, *object_schema, object);
}
Object child = Object::create<JSValueRef>(ctx, realm, *object_schema, (JSValueRef)object, try_update);
return child.row().get_index();
}
template<> JSValueRef RJSAccessor::from_object(JSContextRef ctx, Object object) {
return RJSObjectCreate(ctx, object);
}
template<> size_t RJSAccessor::list_size(JSContextRef ctx, JSValueRef &val) {
return RJSValidatedListLength(ctx, RJSValidatedValueToObject(ctx, val));
}
template<> JSValueRef RJSAccessor::list_value_at_index(JSContextRef ctx, JSValueRef &val, size_t index) {
return RJSValidatedObjectAtIndex(ctx, RJSValidatedValueToObject(ctx, val), (unsigned int)index);
}
template<> JSValueRef RJSAccessor::from_list(JSContextRef ctx, List list) {
return RJSListCreate(ctx, list);
}
}

144
src/js_object_accessor.hpp Normal file
View File

@ -0,0 +1,144 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "js_list.hpp"
#include "js_realm_object.hpp"
#include "js_schema.hpp"
namespace realm {
namespace js {
template<typename T>
struct NativeAccessor {
using ContextType = typename T::Context;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using Object = js::Object<T>;
using Value = js::Value<T>;
static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) {
ObjectType object = Value::validated_to_object(ctx, dict);
return Object::has_property(ctx, object, prop_name);
}
static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name) {
ObjectType object = Value::validated_to_object(ctx, dict);
return Object::get_property(ctx, object, prop_name);
}
static bool has_default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
auto defaults = get_delegate<T>(realm)->m_defaults[object_schema.name];
return defaults.count(prop_name) != 0;
}
static ValueType default_value_for_property(ContextType ctx, realm::Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
auto defaults = get_delegate<T>(realm)->m_defaults[object_schema.name];
return defaults.at(prop_name);
}
// These must be implemented for each JS engine.
static std::string to_binary(ContextType, ValueType &);
static ValueType from_binary(ContextType, BinaryData);
static bool to_bool(ContextType ctx, ValueType &value) {
return Value::validated_to_boolean(ctx, value, "Property");
}
static ValueType from_bool(ContextType ctx, bool boolean) {
return Value::from_boolean(ctx, boolean);
}
static long long to_long(ContextType ctx, ValueType &value) {
return Value::validated_to_number(ctx, value, "Property");
}
static ValueType from_long(ContextType ctx, long long number) {
return Value::from_number(ctx, number);
}
static float to_float(ContextType ctx, ValueType &value) {
return Value::validated_to_number(ctx, value, "Property");
}
static ValueType from_float(ContextType ctx, float number) {
return Value::from_number(ctx, number);
}
static double to_double(ContextType ctx, ValueType &value) {
return Value::validated_to_number(ctx, value, "Property");
}
static ValueType from_double(ContextType ctx, double number) {
return Value::from_number(ctx, number);
}
static std::string to_string(ContextType ctx, ValueType &value) {
return Value::validated_to_string(ctx, value, "Property");
}
static ValueType from_string(ContextType ctx, StringData string) {
return Value::from_string(ctx, string.data());
}
static DateTime to_datetime(ContextType ctx, ValueType &value) {
ObjectType date = Value::validated_to_date(ctx, value, "Property");
return DateTime(Value::to_number(ctx, date));
}
static ValueType from_datetime(ContextType ctx, DateTime dt) {
return Object::create_date(ctx, dt.get_datetime());
}
static bool is_null(ContextType ctx, ValueType &value) {
return Value::is_null(ctx, value) || Value::is_undefined(ctx, value);
}
static ValueType null_value(ContextType ctx) {
return Value::from_null(ctx);
}
static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &value, const std::string &type, bool try_update) {
ObjectType object = Value::validated_to_object(ctx, value);
if (Object::template is_instance<RealmObjectClass<T>>(ctx, object)) {
return get_internal<T, RealmObjectClass<T>>(object)->row().get_index();
}
auto object_schema = realm->config().schema->find(type);
if (Value::is_array(ctx, object)) {
object = Schema<T>::dict_for_property_array(ctx, *object_schema, object);
}
auto child = realm::Object::create<ValueType>(ctx, realm, *object_schema, static_cast<ValueType>(object), try_update);
return child.row().get_index();
}
static size_t to_existing_object_index(ContextType ctx, ValueType &value) {
ObjectType object = Value::validated_to_object(ctx, value);
if (Object::template is_instance<RealmObjectClass<T>>(ctx, object)) {
return get_internal<T, RealmObjectClass<T>>(object)->row().get_index();
}
throw std::runtime_error("object is not a Realm Object");
}
static ValueType from_object(ContextType ctx, realm::Object realm_object) {
return RealmObject<T>::create_instance(ctx, realm_object);
}
static size_t list_size(ContextType ctx, ValueType &value) {
return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value));
}
static ValueType list_value_at_index(ContextType ctx, ValueType &value, size_t index) {
return Object::validated_get_object(ctx, Value::validated_to_object(ctx, value), (uint32_t)index);
}
static ValueType from_list(ContextType ctx, realm::List list) {
return List<T>::create_instance(ctx, list);
}
static Mixed to_mixed(ContextType ctx, ValueType &val) {
throw std::runtime_error("'Any' type is unsupported");
}
};
} // js
} // realm

View File

@ -16,554 +16,29 @@
//
////////////////////////////////////////////////////////////////////////////
#include "js_realm.hpp"
#include "js_object.hpp"
#include "js_results.hpp"
#include "js_list.hpp"
#include "js_schema.hpp"
#include "platform.hpp"
#include "realm_coordinator.hpp"
#include "shared_realm.hpp"
#include "impl/realm_coordinator.hpp"
#include "object_accessor.hpp"
#include "binding_context.hpp"
namespace realm {
namespace js {
#include <set>
#include <cassert>
static std::string s_default_path = "";
using namespace realm;
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
class RJSRealmDelegate : public BindingContext {
public:
virtual void did_change(std::vector<ObserverState> const& observers, std::vector<void*> const& invalidated) {
notify("change");
std::string default_path() {
if (s_default_path.size() == 0) {
s_default_path = realm::default_realm_file_directory() + "/default.realm";
}
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;
return s_default_path;
}
std::map<std::string, JSObjectRef> &RJSConstructors(Realm *realm) {
return static_cast<RJSRealmDelegate *>(realm->m_binding_context.get())->m_constructors;
void set_default_path(std::string path) {
s_default_path = path;
}
// 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;
void delete_all_realms() {
realm::_impl::RealmCoordinator::clear_all_caches();
realm::remove_realm_files_from_directory(realm::default_realm_file_directory());
}
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;
}
} // js
} // realm

View File

@ -18,19 +18,503 @@
#pragma once
#include "js_util.hpp"
#include <list>
#include <map>
#include "js_class.hpp"
#include "js_types.hpp"
#include "js_util.hpp"
#include "js_realm_object.hpp"
#include "js_list.hpp"
#include "js_results.hpp"
#include "js_schema.hpp"
#include "shared_realm.hpp"
#include "binding_context.hpp"
#include "object_accessor.hpp"
#include "platform.hpp"
namespace realm {
class Realm;
using ObjectDefaults = std::map<std::string, JSValueRef>;
namespace js {
template<typename T>
struct RealmClass;
template<typename T>
class RealmDelegate : public BindingContext {
public:
using GlobalContextType = typename T::GlobalContext;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using Value = js::Value<T>;
using ObjectDefaultsMap = typename Schema<T>::ObjectDefaultsMap;
using ConstructorMap = typename Schema<T>::ConstructorMap;
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) {}
RealmDelegate(std::weak_ptr<Realm> realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {}
~RealmDelegate() {
// All protected values need to be unprotected while the context is retained.
m_defaults.clear();
m_constructors.clear();
m_notifications.clear();
}
void add_notification(FunctionType notification) {
for (auto &handler : m_notifications) {
if (handler == notification) {
return;
}
}
m_notifications.emplace_back(m_context, notification);
}
void remove_notification(FunctionType notification) {
for (auto iter = m_notifications.begin(); iter != m_notifications.end(); ++iter) {
if (*iter == notification) {
m_notifications.erase(iter);
return;
}
}
}
void remove_all_notifications() {
m_notifications.clear();
}
ObjectDefaultsMap m_defaults;
ConstructorMap m_constructors;
private:
Protected<GlobalContextType> m_context;
std::list<Protected<FunctionType>> m_notifications;
std::weak_ptr<Realm> m_realm;
void notify(const char *notification_name) {
SharedRealm realm = m_realm.lock();
if (!realm) {
throw std::runtime_error("Realm no longer exists");
}
ObjectType realm_object = create_object<T, RealmClass<T>>(m_context, new SharedRealm(realm));
ValueType arguments[2];
arguments[0] = realm_object;
arguments[1] = Value::from_string(m_context, notification_name);
for (auto &callback : m_notifications) {
Function<T>::call(m_context, callback, realm_object, 2, arguments);
}
}
};
std::string default_path();
void set_default_path(std::string path);
void delete_all_realms();
template<typename T>
class Realm {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using String = js::String<T>;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ReturnValue = js::ReturnValue<T>;
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
public:
static FunctionType create_constructor(ContextType);
// methods
static void objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_one(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_all(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void write(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void close(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
// properties
static void get_path(ContextType, ObjectType, ReturnValue &);
static void get_schema_version(ContextType, ObjectType, ReturnValue &);
// static methods
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
static void schema_version(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void clear_test_state(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
// static properties
static void get_default_path(ContextType, ObjectType, ReturnValue &);
static void set_default_path(ContextType, ObjectType, ValueType value);
private:
static std::string validated_notification_name(ContextType ctx, const ValueType &value) {
std::string name = Value::validated_to_string(ctx, value, "notification name");
if (name != "change") {
throw std::runtime_error("Only the 'change' notification name is supported.");
}
return name;
}
// converts constructor object or type name to type name
static std::string validated_object_type_for_value(SharedRealm &realm, ContextType ctx, const ValueType &value) {
if (Value::is_constructor(ctx, value)) {
FunctionType constructor = Value::to_constructor(ctx, value);
auto delegate = get_delegate<T>(realm.get());
for (auto &pair : delegate->m_constructors) {
if (FunctionType(pair.second) == constructor) {
return pair.first;
}
}
throw std::runtime_error("Constructor was not registered in the schema for this Realm");
}
return Value::validated_to_string(ctx, value, "objectType");
}
static std::string normalize_path(std::string path) {
if (path.size() && path[0] != '/') {
return default_realm_file_directory() + "/" + path;
}
return path;
}
};
template<typename T>
struct RealmClass : ClassDefinition<T, SharedRealm> {
using Realm = js::Realm<T>;
std::string const name = "Realm";
ConstructorType<T>* const constructor = Realm::constructor;
MethodMap<T> const static_methods = {
{"schemaVersion", wrap<Realm::schema_version>},
{"clearTestState", wrap<Realm::clear_test_state>},
};
PropertyMap<T> const static_properties = {
{"defaultPath", {wrap<Realm::get_default_path>, wrap<Realm::set_default_path>}},
};
MethodMap<T> const methods = {
{"objects", wrap<Realm::objects>},
{"create", wrap<Realm::create>},
{"delete", wrap<Realm::delete_one>},
{"deleteAll", wrap<Realm::delete_all>},
{"write", wrap<Realm::write>},
{"addListener", wrap<Realm::add_listener>},
{"removeListener", wrap<Realm::remove_listener>},
{"removeAllListeners", wrap<Realm::remove_all_listeners>},
{"close", wrap<Realm::close>},
};
PropertyMap<T> const properties = {
{"path", {wrap<Realm::get_path>, nullptr}},
{"schemaVersion", {wrap<Realm::get_schema_version>, nullptr}},
};
};
template<typename T>
inline typename T::Function Realm<T>::create_constructor(ContextType ctx) {
FunctionType realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
FunctionType collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
FunctionType list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
FunctionType results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete);
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes);
Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes);
return realm_constructor;
}
JSClassRef RJSRealmClass();
JSClassRef RJSRealmConstructorClass();
template<typename T>
void Realm<T>::constructor(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[]) {
static const String path_string = "path";
static const String schema_string = "schema";
static const String schema_version_string = "schemaVersion";
static const String encryption_key_string = "encryptionKey";
std::string RJSDefaultPath();
void RJSSetDefaultPath(std::string path);
realm::Realm::Config config;
typename Schema<T>::ObjectDefaultsMap defaults;
typename Schema<T>::ConstructorMap constructors;
std::map<std::string, realm::ObjectDefaults> &RJSDefaults(realm::Realm *realm);
std::map<std::string, JSObjectRef> &RJSConstructors(realm::Realm *realm);
if (argc == 0) {
config.path = default_path();
}
else if (argc == 1) {
ValueType value = arguments[0];
if (Value::is_string(ctx, value)) {
config.path = Value::validated_to_string(ctx, value, "path");
}
else if (Value::is_object(ctx, value)) {
ObjectType object = Value::validated_to_object(ctx, value);
ValueType path_value = Object::get_property(ctx, object, path_string);
if (!Value::is_undefined(ctx, path_value)) {
config.path = Value::validated_to_string(ctx, path_value, "path");
}
else {
config.path = js::default_path();
}
ValueType schema_value = Object::get_property(ctx, object, schema_string);
if (!Value::is_undefined(ctx, schema_value)) {
ObjectType schema_object = Value::validated_to_object(ctx, schema_value, "schema");
config.schema.reset(new realm::Schema(Schema<T>::parse_schema(ctx, schema_object, defaults, constructors)));
}
ValueType version_value = Object::get_property(ctx, object, schema_version_string);
if (!Value::is_undefined(ctx, version_value)) {
config.schema_version = Value::validated_to_number(ctx, version_value, "schemaVersion");
}
else {
config.schema_version = 0;
}
ValueType encryption_key_value = Object::get_property(ctx, object, encryption_key_string);
if (!Value::is_undefined(ctx, encryption_key_value)) {
std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value);
config.encryption_key = std::vector<char>(encryption_key.begin(), encryption_key.end());
}
}
}
else {
throw std::runtime_error("Invalid arguments when constructing 'Realm'");
}
config.path = normalize_path(config.path);
ensure_directory_exists_for_file(config.path);
SharedRealm realm = realm::Realm::get_shared_realm(config);
auto delegate = new RealmDelegate<T>(realm, Context<T>::get_global_context(ctx));
if (!realm->m_binding_context) {
realm->m_binding_context.reset(delegate);
}
delegate->m_defaults = std::move(defaults);
delegate->m_constructors = std::move(constructors);
set_internal<T, RealmClass<T>>(this_object, new SharedRealm(realm));
}
template<typename T>
void Realm<T>::schema_version(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
realm::Realm::Config config;
config.path = normalize_path(Value::validated_to_string(ctx, arguments[0]));
if (argc == 2) {
auto encryptionKeyValue = arguments[1];
std::string encryptionKey = NativeAccessor::to_binary(ctx, encryptionKeyValue);
config.encryption_key = std::vector<char>(encryptionKey.begin(), encryptionKey.end());
}
auto version = realm::Realm::get_schema_version(config);
if (version == ObjectStore::NotVersioned) {
return_value.set(-1);
}
else {
return_value.set((double)version);
}
}
template<typename T>
void Realm<T>::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
delete_all_realms();
}
template<typename T>
void Realm<T>::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) {
return_value.set(realm::js::default_path());
}
template<typename T>
void Realm<T>::set_default_path(ContextType ctx, ObjectType object, ValueType value) {
js::set_default_path(Value::validated_to_string(ctx, value, "defaultPath"));
}
template<typename T>
void Realm<T>::get_path(ContextType ctx, ObjectType object, ReturnValue &return_value) {
std::string path = get_internal<T, RealmClass<T>>(object)->get()->config().path;
return_value.set(path);
}
template<typename T>
void Realm<T>::get_schema_version(ContextType ctx, ObjectType object, ReturnValue &return_value) {
double version = get_internal<T, RealmClass<T>>(object)->get()->config().schema_version;
return_value.set(version);
}
template<typename T>
void Realm<T>::objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
std::string type = validated_object_type_for_value(realm, ctx, arguments[0]);
return_value.set(Results<T>::create_instance(ctx, realm, type));
}
template<typename T>
void Realm<T>::create(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2, 3);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
std::string className = validated_object_type_for_value(realm, ctx, arguments[0]);
auto &schema = realm->config().schema;
auto object_schema = schema->find(className);
if (object_schema == schema->end()) {
throw std::runtime_error("Object type '" + className + "' not found in schema.");
}
ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties");
if (Value::is_array(ctx, arguments[1])) {
object = Schema<T>::dict_for_property_array(ctx, *object_schema, object);
}
bool update = false;
if (argc == 3) {
update = Value::validated_to_boolean(ctx, arguments[2], "update");
}
auto realm_object = realm::Object::create<ValueType>(ctx, realm, *object_schema, object, update);
return_value.set(RealmObject<T>::create_instance(ctx, realm_object));
}
template<typename T>
void Realm<T>::delete_one(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
if (!realm->is_in_transaction()) {
throw std::runtime_error("Can only delete objects within a transaction.");
}
ObjectType arg = Value::validated_to_object(ctx, arguments[0]);
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
auto object = get_internal<T, RealmObjectClass<T>>(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 (Value::is_array(ctx, arg)) {
uint32_t length = Object::validated_get_length(ctx, arg);
for (uint32_t i = length; i--;) {
ObjectType object = Object::validated_get_object(ctx, arg, i);
if (!Object::template is_instance<RealmObjectClass<T>>(ctx, object)) {
throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects.");
}
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
realm::TableRef table = ObjectStore::table_for_object_type(realm->read_group(), realm_object->get_object_schema().name);
table->move_last_over(realm_object->row().get_index());
}
}
else if (Object::template is_instance<ResultsClass<T>>(ctx, arg)) {
auto results = get_internal<T, ResultsClass<T>>(arg);
results->clear();
}
else if (Object::template is_instance<ListClass<T>>(ctx, arg)) {
auto list = get_internal<T, ListClass<T>>(arg);
list->delete_all();
}
else {
throw std::runtime_error("Argument to 'delete' must be a Realm object or a collection of Realm objects.");
}
}
template<typename T>
void Realm<T>::delete_all(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
if (!realm->is_in_transaction()) {
throw std::runtime_error("Can only delete objects within a transaction.");
}
for (auto objectSchema : *realm->config().schema) {
ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear();
}
}
template<typename T>
void Realm<T>::write(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
FunctionType callback = Value::validated_to_function(ctx, arguments[0]);
realm->begin_transaction();
try {
Function<T>::call(ctx, callback, this_object, 0, nullptr);
}
catch (std::exception &e) {
realm->cancel_transaction();
throw e;
}
realm->commit_transaction();
}
template<typename T>
void Realm<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
__unused std::string name = validated_notification_name(ctx, arguments[0]);
auto callback = Value::validated_to_function(ctx, arguments[1]);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
get_delegate<T>(realm.get())->add_notification(callback);
}
template<typename T>
void Realm<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
__unused std::string name = validated_notification_name(ctx, arguments[0]);
auto callback = Value::validated_to_function(ctx, arguments[1]);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
get_delegate<T>(realm.get())->remove_notification(callback);
}
template<typename T>
void Realm<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0, 1);
if (argc) {
validated_notification_name(ctx, arguments[0]);
}
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
get_delegate<T>(realm.get())->remove_all_notifications();
}
template<typename T>
void Realm<T>::close(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
realm->close();
}
} // js
} // realm

122
src/js_realm_object.hpp Normal file
View File

@ -0,0 +1,122 @@
////////////////////////////////////////////////////////////////////////////
//
// 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_class.hpp"
#include "js_types.hpp"
#include "js_util.hpp"
#include "object_accessor.hpp"
#include "object_store.hpp"
namespace realm {
namespace js {
template<typename T>
class RealmObject {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using String = js::String<T>;
using Value = js::Value<T>;
using Object = js::Object<T>;
using Function = js::Function<T>;
using ReturnValue = js::ReturnValue<T>;
public:
static ObjectType create_instance(ContextType, realm::Object &);
static void get_property(ContextType, ObjectType, const String &, ReturnValue &);
static bool set_property(ContextType, ObjectType, const String &, ValueType);
static std::vector<String> get_property_names(ContextType, ObjectType);
};
template<typename T>
struct RealmObjectClass : ClassDefinition<T, realm::Object> {
using RealmObject = js::RealmObject<T>;
const std::string name = "RealmObject";
const StringPropertyType<T> string_accessor = {
wrap<RealmObject::get_property>,
wrap<RealmObject::set_property>,
wrap<RealmObject::get_property_names>,
};
};
template<typename T>
typename T::Object RealmObject<T>::create_instance(ContextType ctx, realm::Object &realm_object) {
static String prototype_string = "prototype";
auto delegate = get_delegate<T>(realm_object.realm().get());
auto name = realm_object.get_object_schema().name;
auto object = create_object<T, RealmObjectClass<T>>(ctx, new realm::Object(realm_object));
if (!delegate->m_constructors.count(name)) {
return object;
}
FunctionType constructor = delegate->m_constructors.at(name);
ObjectType prototype = Object::validated_get_object(ctx, constructor, prototype_string);
Object::set_prototype(ctx, object, prototype);
ValueType result = Function::call(ctx, constructor, object, 0, NULL);
if (result != object && !Value::is_null(ctx, result) && !Value::is_undefined(ctx, result)) {
throw std::runtime_error("Realm object constructor must not return another value");
}
return object;
}
template<typename T>
void RealmObject<T>::get_property(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) {
try {
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
auto result = realm_object->template get_property_value<ValueType>(ctx, property);
return_value.set(result);
} catch (InvalidPropertyException &ex) {
// getters for nonexistent properties in JS should always return undefined
}
}
template<typename T>
bool RealmObject<T>::set_property(ContextType ctx, ObjectType object, const String &property, ValueType value) {
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
realm_object->set_property_value(ctx, property, value, true);
return true;
}
template<typename T>
std::vector<String<T>> RealmObject<T>::get_property_names(ContextType ctx, ObjectType object) {
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
auto &properties = realm_object->get_object_schema().properties;
std::vector<String> names;
names.reserve(properties.size());
for (auto &prop : properties) {
names.push_back(prop.name);
}
return names;
}
} // js
} // realm

View File

@ -1,249 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#include "js_results.hpp"
#include "js_collection.hpp"
#include "js_object.hpp"
#include "object_accessor.hpp"
#include "results.hpp"
#include "parser.hpp"
#include "query_builder.hpp"
using namespace realm;
JSValueRef ResultsGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(object);
std::string indexStr = RJSStringForJSString(propertyName);
if (indexStr == "length") {
return JSValueMakeNumber(ctx, results->size());
}
auto row = results->get(RJSValidatedPositiveIndex(indexStr));
if (!row.is_attached()) {
return JSValueMakeNull(ctx);
}
return RJSObjectCreate(ctx, Object(results->get_realm(), results->get_object_schema(), row));
}
catch (std::out_of_range &exp) {
// getters for nonexistent properties in JS should always return undefined
return JSValueMakeUndefined(ctx);
}
catch (std::invalid_argument &exp) {
// for stol failure this could be another property that is handled externally, so ignore
return NULL;
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
return NULL;
}
}
bool ResultsSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef *jsException) {
try {
std::string indexStr = RJSStringForJSString(propertyName);
if (indexStr != "length") {
stot<long>(RJSStringForJSString(propertyName));
}
// attempts to assign to 'length' or an index should throw an exception
if (jsException) {
*jsException = RJSMakeError(ctx, "Results objects are readonly");
}
}
catch (std::invalid_argument &exp) {
// for stol failure this could be another property that is handled externally, so ignore
}
return false;
}
void ResultsPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) {
Results *results = RJSGetInternal<Results *>(object);
size_t size = results->size();
char str[32];
for (size_t i = 0; i < size; i++) {
sprintf(str, "%zu", i);
JSStringRef name = JSStringCreateWithUTF8CString(str);
JSPropertyNameAccumulatorAddName(propertyNames, name);
JSStringRelease(name);
}
}
JSValueRef ResultsStaticCopy(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(thisObject);
RJSValidateArgumentCount(argumentCount, 0);
Results *copy = new Results(*results);
copy->set_live(false);
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), copy);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ResultsSorted(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(thisObject);
RJSValidateArgumentRange(argumentCount, 1, 2);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
return RJSResultsCreateSorted(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSValueRef ResultsFiltered(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
Results *results = RJSGetInternal<Results *>(thisObject);
RJSValidateArgumentCountIsAtLeast(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
return RJSResultsCreateFiltered(ctx, sharedRealm, results->get_object_schema(), std::move(results->get_query()), argumentCount, arguments);
}
catch (std::exception &exp) {
if (jsException) {
*jsException = RJSMakeError(ctx, exp);
}
}
return NULL;
}
JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className) {
TableRef table = ObjectStore::table_for_object_type(realm->read_group(), className);
auto object_schema = realm->config().schema->find(className);
if (object_schema == realm->config().schema->end()) {
throw std::runtime_error("Object type '" + className + "' not present in Realm.");
}
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, *table));
}
JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, std::string className, std::string queryString, std::vector<JSValueRef> args) {
TableRef table = ObjectStore::table_for_object_type(realm->read_group(), className);
Query query = table->where();
const Schema &schema = *realm->config().schema;
auto object_schema = schema.find(className);
if (object_schema == schema.end()) {
throw std::runtime_error("Object type '" + className + "' not present in Realm.");
}
parser::Predicate predicate = parser::parse(queryString);
query_builder::ArgumentConverter<JSValueRef, JSContextRef> arguments(ctx, args);
query_builder::apply_predicate(query, predicate, arguments, schema, object_schema->name);
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), new Results(realm, *object_schema, std::move(query)));
}
JSObjectRef RJSResultsCreate(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, bool live) {
Results *results = new Results(realm, objectSchema, std::move(query));
results->set_live(live);
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), results);
}
JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) {
std::string queryString = RJSValidatedStringForValue(ctx, arguments[0], "predicate");
std::vector<JSValueRef> args(argumentCount - 1);
for (size_t i = 1; i < argumentCount; i++) {
args[i-1] = arguments[i];
}
parser::Predicate predicate = parser::parse(queryString);
query_builder::ArgumentConverter<JSValueRef, JSContextRef> queryArgs(ctx, args);
query_builder::apply_predicate(query, predicate, queryArgs, *realm->config().schema, objectSchema.name);
return RJSResultsCreate(ctx, realm, objectSchema, std::move(query));
}
JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, SharedRealm realm, const ObjectSchema &objectSchema, Query query, size_t argumentCount, const JSValueRef arguments[]) {
size_t prop_count;
std::vector<std::string> prop_names;
std::vector<bool> ascending;
if (RJSIsValueArray(ctx, arguments[0])) {
RJSValidateArgumentCount(argumentCount, 1, "Second argument is not allowed if passed an array of sort descriptors");
JSObjectRef js_prop_names = RJSValidatedValueToObject(ctx, arguments[0]);
prop_count = RJSValidatedListLength(ctx, js_prop_names);
if (!prop_count) {
throw std::invalid_argument("Sort descriptor array must not be empty");
}
prop_names.resize(prop_count);
ascending.resize(prop_count);
for (unsigned int i = 0; i < prop_count; i++) {
JSValueRef val = RJSValidatedPropertyAtIndex(ctx, js_prop_names, i);
if (RJSIsValueArray(ctx, val)) {
prop_names[i] = RJSValidatedStringForValue(ctx, RJSValidatedPropertyAtIndex(ctx, (JSObjectRef)val, 0));
ascending[i] = !JSValueToBoolean(ctx, RJSValidatedPropertyAtIndex(ctx, (JSObjectRef)val, 1));
}
else {
prop_names[i] = RJSValidatedStringForValue(ctx, val);
ascending[i] = true;
}
}
}
else {
RJSValidateArgumentRange(argumentCount, 1, 2);
prop_count = 1;
prop_names.push_back(RJSValidatedStringForValue(ctx, arguments[0]));
ascending.push_back(argumentCount == 1 ? true : !JSValueToBoolean(ctx, arguments[1]));
}
std::vector<size_t> columns(prop_count);
size_t index = 0;
for (std::string prop_name : prop_names) {
const Property *prop = objectSchema.property_for_name(prop_name);
if (!prop) {
throw std::runtime_error("Property '" + prop_name + "' does not exist on object type '" + objectSchema.name + "'");
}
columns[index++] = prop->table_column;
}
Results *results = new Results(realm, objectSchema, std::move(query), {std::move(columns), std::move(ascending)});
return RJSWrapObject<Results *>(ctx, RJSResultsClass(), results);
}
static const JSStaticFunction RJSResultsFuncs[] = {
{"snapshot", ResultsStaticCopy, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"sorted", ResultsSorted, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{"filtered", ResultsFiltered, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
{NULL, NULL},
};
JSClassRef RJSResultsClass() {
static JSClassRef s_objectClass = RJSCreateWrapperClass<Results *>("Results", ResultsGetProperty, ResultsSetProperty, RJSResultsFuncs, ResultsPropertyNames, RJSCollectionClass());
return s_objectClass;
}

View File

@ -18,18 +18,226 @@
#pragma once
#include "js_util.hpp"
#include <memory>
#include "js_collection.hpp"
#include "js_realm_object.hpp"
#include "results.hpp"
#include "list.hpp"
#include "parser.hpp"
#include "query_builder.hpp"
namespace realm {
class Realm;
class Query;
typedef std::shared_ptr<Realm> SharedRealm;
namespace js {
template<typename T>
class Results {
using ContextType = typename T::Context;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ReturnValue = js::ReturnValue<T>;
public:
static ObjectType create_instance(ContextType, const realm::Results &, bool live = true);
static ObjectType create_instance(ContextType, const realm::List &, bool live = true);
static ObjectType create_instance(ContextType, SharedRealm, const std::string &type, bool live = true);
static ObjectType create_instance(ContextType, SharedRealm, const ObjectSchema &, Query, bool live = true);
template<typename U>
static ObjectType create_filtered(ContextType, const U &, size_t, const ValueType[]);
template<typename U>
static ObjectType create_sorted(ContextType, const U &, size_t, const ValueType[]);
static void get_length(ContextType, ObjectType, ReturnValue &);
static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &);
static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
};
template<typename T>
struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
using Results = js::Results<T>;
std::string const name = "Results";
MethodMap<T> const methods = {
{"snapshot", wrap<Results::snapshot>},
{"filtered", wrap<Results::filtered>},
{"sorted", wrap<Results::sorted>},
};
PropertyMap<T> const properties = {
{"length", {wrap<Results::get_length>, nullptr}},
};
IndexPropertyType<T> const index_accessor = {wrap<Results::get_index>, nullptr};
};
template<typename T>
typename T::Object Results<T>::create_instance(ContextType ctx, const realm::Results &results, bool live) {
auto new_results = new realm::Results(results);
new_results->set_live(live);
return create_object<T, ResultsClass<T>>(ctx, new_results);
}
JSClassRef RJSResultsClass();
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className);
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, std::string className, std::string query, std::vector<JSValueRef> args);
JSObjectRef RJSResultsCreate(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, bool live = true);
JSObjectRef RJSResultsCreateFiltered(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, size_t argumentCount, const JSValueRef arguments[]);
JSObjectRef RJSResultsCreateSorted(JSContextRef ctx, realm::SharedRealm realm, const realm::ObjectSchema &objectSchema, realm::Query query, size_t argumentCount, const JSValueRef arguments[]);
template<typename T>
typename T::Object Results<T>::create_instance(ContextType ctx, const realm::List &list, bool live) {
return create_instance(ctx, list.get_realm(), list.get_object_schema(), list.get_query(), live);
}
template<typename T>
typename T::Object Results<T>::create_instance(ContextType ctx, SharedRealm realm, const std::string &type, bool live) {
auto table = ObjectStore::table_for_object_type(realm->read_group(), type);
auto &schema = realm->config().schema;
auto object_schema = schema->find(type);
if (object_schema == schema->end()) {
throw std::runtime_error("Object type '" + type + "' not present in Realm.");
}
auto results = new realm::Results(realm, *object_schema, *table);
results->set_live(live);
return create_object<T, ResultsClass<T>>(ctx, results);
}
template<typename T>
typename T::Object Results<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, Query query, bool live) {
auto results = new realm::Results(realm, object_schema, std::move(query));
results->set_live(live);
return create_object<T, ResultsClass<T>>(ctx, results);
}
template<typename T>
template<typename U>
typename T::Object Results<T>::create_filtered(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) {
auto query_string = Value::validated_to_string(ctx, arguments[0], "predicate");
auto query = collection.get_query();
auto const &realm = collection.get_realm();
auto const &object_schema = collection.get_object_schema();
std::vector<ValueType> args;
args.reserve(argc - 1);
for (size_t i = 1; i < argc; i++) {
args.push_back(arguments[i]);
}
parser::Predicate predicate = parser::parse(query_string);
query_builder::ArgumentConverter<ValueType, ContextType> converter(ctx, args);
query_builder::apply_predicate(query, predicate, converter, *realm->config().schema, object_schema.name);
return create_instance(ctx, realm, object_schema, std::move(query));
}
template<typename T>
template<typename U>
typename T::Object Results<T>::create_sorted(ContextType ctx, const U &collection, size_t argc, const ValueType arguments[]) {
auto const &realm = collection.get_realm();
auto const &object_schema = collection.get_object_schema();
std::vector<std::string> prop_names;
std::vector<bool> ascending;
size_t prop_count;
if (Value::is_array(ctx, arguments[0])) {
validate_argument_count(argc, 1, "Second argument is not allowed if passed an array of sort descriptors");
ObjectType js_prop_names = Value::validated_to_object(ctx, arguments[0]);
prop_count = Object::validated_get_length(ctx, js_prop_names);
if (!prop_count) {
throw std::invalid_argument("Sort descriptor array must not be empty");
}
prop_names.resize(prop_count);
ascending.resize(prop_count);
for (unsigned int i = 0; i < prop_count; i++) {
ValueType value = Object::validated_get_property(ctx, js_prop_names, i);
if (Value::is_array(ctx, value)) {
ObjectType array = Value::to_array(ctx, value);
prop_names[i] = Object::validated_get_string(ctx, array, 0);
ascending[i] = !Object::validated_get_boolean(ctx, array, 1);
}
else {
prop_names[i] = Value::validated_to_string(ctx, value);
ascending[i] = true;
}
}
}
else {
validate_argument_count(argc, 1, 2);
prop_count = 1;
prop_names.push_back(Value::validated_to_string(ctx, arguments[0]));
ascending.push_back(argc == 1 ? true : !Value::to_boolean(ctx, arguments[1]));
}
std::vector<size_t> columns;
columns.reserve(prop_count);
for (std::string &prop_name : prop_names) {
const Property *prop = object_schema.property_for_name(prop_name);
if (!prop) {
throw std::runtime_error("Property '" + prop_name + "' does not exist on object type '" + object_schema.name + "'");
}
columns.push_back(prop->table_column);
}
auto results = new realm::Results(realm, object_schema, collection.get_query(), {std::move(columns), std::move(ascending)});
return create_object<T, ResultsClass<T>>(ctx, results);
}
template<typename T>
void Results<T>::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) {
auto results = get_internal<T, ResultsClass<T>>(object);
return_value.set((uint32_t)results->size());
}
template<typename T>
void Results<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
auto results = get_internal<T, ResultsClass<T>>(object);
auto row = results->get(index);
// Return null for deleted objects in a snapshot.
if (!row.is_attached()) {
return_value.set_null();
return;
}
auto realm_object = realm::Object(results->get_realm(), results->get_object_schema(), results->get(index));
return_value.set(RealmObject<T>::create_instance(ctx, realm_object));
}
template<typename T>
void Results<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto results = get_internal<T, ResultsClass<T>>(this_object);
return_value.set(Results<T>::create_instance(ctx, *results, false));
}
template<typename T>
void Results<T>::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto results = get_internal<T, ResultsClass<T>>(this_object);
return_value.set(create_filtered(ctx, *results, argc, arguments));
}
template<typename T>
void Results<T>::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
auto results = get_internal<T, ResultsClass<T>>(this_object);
return_value.set(create_sorted(ctx, *results, argc, arguments));
}
} // js
} // realm

View File

@ -1,206 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#include "js_schema.hpp"
#include "object_store.hpp"
namespace realm {
struct SchemaWrapper {
Schema *schema;
bool owned;
~SchemaWrapper() {
if (owned) {
delete schema;
}
}
};
}
using namespace realm;
JSClassRef RJSSchemaClass() {
static JSClassRef s_schemaClass = RJSCreateWrapperClass<SchemaWrapper *>("Schema");
return s_schemaClass;
}
JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) {
SchemaWrapper *wrapper = new SchemaWrapper();
wrapper->schema = &schema;
wrapper->owned = false;
return RJSWrapObject(ctx, RJSSchemaClass(), wrapper);
}
static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) {
static JSStringRef defaultString = JSStringCreateWithUTF8CString("default");
static JSStringRef indexedString = JSStringCreateWithUTF8CString("indexed");
static JSStringRef typeString = JSStringCreateWithUTF8CString("type");
static JSStringRef objectTypeString = JSStringCreateWithUTF8CString("objectType");
static JSStringRef optionalString = JSStringCreateWithUTF8CString("optional");
Property prop;
prop.name = propertyName;
JSObjectRef propertyObject = NULL;
std::string type;
if (JSValueIsObject(ctx, propertyAttributes)) {
propertyObject = RJSValidatedValueToObject(ctx, propertyAttributes);
type = RJSValidatedStringProperty(ctx, propertyObject, typeString);
JSValueRef optionalValue = JSObjectGetProperty(ctx, propertyObject, optionalString, NULL);
if (!JSValueIsUndefined(ctx, optionalValue)) {
if (!JSValueIsBoolean(ctx, optionalValue)) {
throw std::runtime_error("'optional' designation expected to be of type boolean");
}
prop.is_nullable = JSValueToBoolean(ctx, optionalValue);
}
}
else {
type = RJSValidatedStringForValue(ctx, propertyAttributes);
}
if (type == "bool") {
prop.type = PropertyTypeBool;
}
else if (type == "int") {
prop.type = PropertyTypeInt;
}
else if (type == "float") {
prop.type = PropertyTypeFloat;
}
else if (type == "double") {
prop.type = PropertyTypeDouble;
}
else if (type == "string") {
prop.type = PropertyTypeString;
}
else if (type == "date") {
prop.type = PropertyTypeDate;
}
else if (type == "data") {
prop.type = PropertyTypeData;
}
else if (type == "list") {
if (!propertyObject) {
throw std::runtime_error("List property must specify 'objectType'");
}
prop.type = PropertyTypeArray;
prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString);
}
else {
prop.type = PropertyTypeObject;
prop.is_nullable = true;
// The type could either be 'object' or the name of another object type in the same schema.
if (type == "object") {
if (!propertyObject) {
throw std::runtime_error("Object property must specify 'objectType'");
}
prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString);
}
else {
prop.object_type = type;
}
}
if (propertyObject) {
JSValueRef defaultValue = RJSValidatedPropertyValue(ctx, propertyObject, defaultString);
if (!JSValueIsUndefined(ctx, defaultValue)) {
JSValueProtect(ctx, defaultValue);
objectDefaults.emplace(prop.name, defaultValue);
}
JSValueRef indexedValue = RJSValidatedPropertyValue(ctx, propertyObject, indexedString);
if (!JSValueIsUndefined(ctx, indexedValue)) {
prop.is_indexed = JSValueToBoolean(ctx, indexedValue);
}
}
return prop;
}
static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSObjectRef> &constructors) {
static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey");
static JSStringRef propertiesString = JSStringCreateWithUTF8CString("properties");
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
JSObjectRef objectConstructor = NULL;
if (JSObjectIsConstructor(ctx, objectSchemaObject)) {
objectConstructor = objectSchemaObject;
objectSchemaObject = RJSValidatedObjectProperty(ctx, objectConstructor, schemaString, "Realm object constructor must have a 'schema' property.");
}
ObjectDefaults objectDefaults;
ObjectSchema objectSchema;
objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString);
JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object.");
if (RJSIsValueArray(ctx, propertiesObject)) {
size_t propertyCount = RJSValidatedListLength(ctx, propertiesObject);
for (size_t i = 0; i < propertyCount; i++) {
JSObjectRef propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i);
std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString);
objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyObject, propertyName, objectDefaults));
}
}
else {
JSPropertyNameArrayRef propertyNames = JSObjectCopyPropertyNames(ctx, propertiesObject);
size_t propertyCount = JSPropertyNameArrayGetCount(propertyNames);
for (size_t i = 0; i < propertyCount; i++) {
JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNames, i);
JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, propertyName);
objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyValue, RJSStringForJSString(propertyName), objectDefaults));
}
JSPropertyNameArrayRelease(propertyNames);
}
JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString);
if (!JSValueIsUndefined(ctx, primaryValue)) {
objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue);
Property *property = objectSchema.primary_key_property();
if (!property) {
throw std::runtime_error("Missing primary key property '" + objectSchema.primary_key + "'");
}
property->is_primary = true;
}
// Store prototype so that objects of this type will have their prototype set to this prototype object.
if (objectConstructor) {
JSValueProtect(ctx, objectConstructor);
constructors[objectSchema.name] = std::move(objectConstructor);
}
defaults.emplace(objectSchema.name, std::move(objectDefaults));
return objectSchema;
}
realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSObjectRef> &constructors) {
std::vector<ObjectSchema> schema;
size_t length = RJSValidatedListLength(ctx, jsonObject);
for (unsigned int i = 0; i < length; i++) {
JSObjectRef jsonObjectSchema = RJSValidatedObjectAtIndex(ctx, jsonObject, i);
ObjectSchema objectSchema = RJSParseObjectSchema(ctx, jsonObjectSchema, defaults, constructors);
schema.emplace_back(std::move(objectSchema));
}
return Schema(schema);
}

View File

@ -18,15 +18,205 @@
#pragma once
#include "js_util.hpp"
#include <map>
#include "js_types.hpp"
#include "schema.hpp"
namespace realm {
class Schema;
using ObjectDefaults = std::map<std::string, JSValueRef>;
namespace js {
template<typename T>
struct Schema {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
using String = js::String<T>;
using Object = js::Object<T>;
using Value = js::Value<T>;
using ObjectDefaults = std::map<std::string, Protected<ValueType>>;
using ObjectDefaultsMap = std::map<std::string, ObjectDefaults>;
using ConstructorMap = std::map<std::string, Protected<FunctionType>>;
static ObjectType dict_for_property_array(ContextType, const ObjectSchema &, ObjectType);
static Property parse_property(ContextType, ValueType, std::string, ObjectDefaults &);
static ObjectSchema parse_object_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &);
static realm::Schema parse_schema(ContextType, ObjectType, ObjectDefaultsMap &, ConstructorMap &);
};
template<typename T>
typename T::Object Schema<T>::dict_for_property_array(ContextType ctx, const ObjectSchema &object_schema, ObjectType array) {
size_t count = object_schema.properties.size();
if (count != Object::validated_get_length(ctx, array)) {
throw std::runtime_error("Array must contain values for all object properties");
}
ObjectType dict = Object::create_empty(ctx);
for (uint32_t i = 0; i < count; i++) {
ValueType value = Object::get_property(ctx, array, i);
Object::set_property(ctx, dict, object_schema.properties[i].name, value);
}
return dict;
}
JSClassRef RJSSchemaClass();
JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema);
template<typename T>
Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::string property_name, ObjectDefaults &object_defaults) {
static const String default_string = "default";
static const String indexed_string = "indexed";
static const String type_string = "type";
static const String object_type_string = "objectType";
static const String optional_string = "optional";
Property prop;
prop.name = property_name;
ObjectType property_object = {};
std::string type;
if (Value::is_object(ctx, attributes)) {
property_object = Value::validated_to_object(ctx, attributes);
type = Object::validated_get_string(ctx, property_object, type_string);
ValueType optional_value = Object::get_property(ctx, property_object, optional_string);
if (!Value::is_undefined(ctx, optional_value)) {
prop.is_nullable = Value::validated_to_boolean(ctx, optional_value, "optional");
}
}
else {
type = Value::validated_to_string(ctx, attributes);
}
if (type == "bool") {
prop.type = PropertyTypeBool;
}
else if (type == "int") {
prop.type = PropertyTypeInt;
}
else if (type == "float") {
prop.type = PropertyTypeFloat;
}
else if (type == "double") {
prop.type = PropertyTypeDouble;
}
else if (type == "string") {
prop.type = PropertyTypeString;
}
else if (type == "date") {
prop.type = PropertyTypeDate;
}
else if (type == "data") {
prop.type = PropertyTypeData;
}
else if (type == "list") {
if (!Value::is_valid(property_object)) {
throw std::runtime_error("List property must specify 'objectType'");
}
prop.type = PropertyTypeArray;
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
}
else {
prop.type = PropertyTypeObject;
prop.is_nullable = true;
// The type could either be 'object' or the name of another object type in the same schema.
if (type == "object") {
if (!Value::is_valid(property_object)) {
throw std::runtime_error("Object property must specify 'objectType'");
}
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
}
else {
prop.object_type = type;
}
}
if (Value::is_valid(property_object)) {
ValueType default_value = Object::get_property(ctx, property_object, default_string);
if (!Value::is_undefined(ctx, default_value)) {
object_defaults.emplace(prop.name, Protected<ValueType>(ctx, default_value));
}
ValueType indexed_value = Object::get_property(ctx, property_object, indexed_string);
if (!Value::is_undefined(ctx, indexed_value)) {
prop.is_indexed = Value::validated_to_boolean(ctx, indexed_value);
}
}
return prop;
}
realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSObjectRef> &constructors);
template<typename T>
ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) {
static const String name_string = "name";
static const String primary_string = "primaryKey";
static const String properties_string = "properties";
static const String schema_string = "schema";
FunctionType object_constructor = {};
if (Value::is_constructor(ctx, object_schema_object)) {
object_constructor = Value::to_constructor(ctx, object_schema_object);
object_schema_object = Object::validated_get_object(ctx, object_constructor, schema_string, "Realm object constructor must have a 'schema' property.");
}
ObjectDefaults object_defaults;
ObjectSchema object_schema;
object_schema.name = Object::validated_get_string(ctx, object_schema_object, name_string);
ObjectType properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema must have a 'properties' object.");
if (Value::is_array(ctx, properties_object)) {
uint32_t length = Object::validated_get_length(ctx, properties_object);
for (uint32_t i = 0; i < length; i++) {
ObjectType property_object = Object::validated_get_object(ctx, properties_object, i);
std::string property_name = Object::validated_get_string(ctx, property_object, name_string);
object_schema.properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults));
}
}
else {
auto property_names = Object::get_property_names(ctx, properties_object);
for (auto &property_name : property_names) {
ValueType property_value = Object::get_property(ctx, properties_object, property_name);
object_schema.properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults));
}
}
ValueType primary_value = Object::get_property(ctx, object_schema_object, primary_string);
if (!Value::is_undefined(ctx, primary_value)) {
object_schema.primary_key = Value::validated_to_string(ctx, primary_value);
Property *property = object_schema.primary_key_property();
if (!property) {
throw std::runtime_error("Missing primary key property '" + object_schema.primary_key + "'");
}
property->is_primary = true;
}
// Store prototype so that objects of this type will have their prototype set to this prototype object.
if (Value::is_valid(object_constructor)) {
constructors.emplace(object_schema.name, Protected<FunctionType>(ctx, object_constructor));
}
defaults.emplace(object_schema.name, std::move(object_defaults));
return object_schema;
}
template<typename T>
realm::Schema Schema<T>::parse_schema(ContextType ctx, ObjectType schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) {
std::vector<ObjectSchema> schema;
uint32_t length = Object::validated_get_length(ctx, schema_object);
for (uint32_t i = 0; i < length; i++) {
ObjectType object_schema_object = Object::validated_get_object(ctx, schema_object, i);
ObjectSchema object_schema = parse_object_schema(ctx, object_schema_object, defaults, constructors);
schema.emplace_back(std::move(object_schema));
}
return realm::Schema(schema);
}
} // js
} // realm

301
src/js_types.hpp Normal file
View File

@ -0,0 +1,301 @@
////////////////////////////////////////////////////////////////////////////
//
// 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 <stdexcept>
#include <string>
#include <vector>
#include <realm/util/to_string.hpp>
#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG)
# define REALM_JS_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER) && !(defined(DEBUG) && DEBUG)
# define REALM_JS_INLINE __forceinline
#else
# define REALM_JS_INLINE inline
#endif
namespace realm {
namespace js {
enum PropertyAttributes {
None = 0,
ReadOnly = 1 << 0,
DontEnum = 1 << 1,
DontDelete = 1 << 2
};
template<typename T>
struct String {
using StringType = typename T::String;
public:
String(const char *);
String(const StringType &);
String(StringType &&);
String(const std::string &);
operator StringType() const;
operator std::string() const;
};
template<typename T>
struct Context {
using ContextType = typename T::Context;
using GlobalContextType = typename T::GlobalContext;
static GlobalContextType get_global_context(ContextType);
};
template<typename T>
struct Value {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
static bool is_array(ContextType, const ValueType &);
static bool is_array_buffer(ContextType, const ValueType &);
static bool is_array_buffer_view(ContextType, const ValueType &);
static bool is_boolean(ContextType, const ValueType &);
static bool is_constructor(ContextType, const ValueType &);
static bool is_date(ContextType, const ValueType &);
static bool is_function(ContextType, const ValueType &);
static bool is_null(ContextType, const ValueType &);
static bool is_number(ContextType, const ValueType &);
static bool is_object(ContextType, const ValueType &);
static bool is_string(ContextType, const ValueType &);
static bool is_undefined(ContextType, const ValueType &);
static bool is_valid(const ValueType &);
static ValueType from_boolean(ContextType, bool);
static ValueType from_null(ContextType);
static ValueType from_number(ContextType, double);
static ValueType from_string(ContextType, const String<T> &);
static ValueType from_undefined(ContextType);
static ObjectType to_array(ContextType, const ValueType &);
static bool to_boolean(ContextType, const ValueType &);
static FunctionType to_constructor(ContextType, const ValueType &);
static ObjectType to_date(ContextType, const ValueType &);
static FunctionType to_function(ContextType, const ValueType &);
static double to_number(ContextType, const ValueType &);
static ObjectType to_object(ContextType, const ValueType &);
static String<T> to_string(ContextType, const ValueType &);
#define VALIDATED(return_t, type) \
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
if (!is_##type(ctx, value)) { \
std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \
throw std::invalid_argument(prefix + " must be: " #type); \
} \
return to_##type(ctx, value); \
}
VALIDATED(ObjectType, array)
VALIDATED(bool, boolean)
VALIDATED(FunctionType, constructor)
VALIDATED(ObjectType, date)
VALIDATED(FunctionType, function)
VALIDATED(double, number)
VALIDATED(ObjectType, object)
VALIDATED(String<T>, string)
#undef VALIDATED
};
template<typename T>
struct Function {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]);
static ValueType call(ContextType ctx, const FunctionType &function, const ObjectType &this_object, const std::vector<ValueType> &arguments) {
return call(ctx, function, this_object, arguments.size(), arguments.data());
}
static ObjectType construct(ContextType, const FunctionType &, size_t, const ValueType[]);
static ValueType construct(ContextType ctx, const FunctionType &function, const std::vector<ValueType> &arguments) {
return construct(ctx, function, arguments.size(), arguments.data());
}
};
template<typename T>
struct Object {
using ContextType = typename T::Context;
using FunctionType = typename T::Function;
using ObjectType = typename T::Object;
using ValueType = typename T::Value;
public:
static ValueType get_prototype(ContextType, const ObjectType &);
static void set_prototype(ContextType, const ObjectType &, const ValueType &);
static bool has_property(ContextType, const ObjectType &, const String<T> &);
static bool has_property(ContextType, const ObjectType &, uint32_t);
static ValueType get_property(ContextType, const ObjectType &, const String<T> &);
static ValueType get_property(ContextType, const ObjectType &, uint32_t);
static void set_property(ContextType, const ObjectType &, const String<T> &, const ValueType &, PropertyAttributes attributes = None);
static void set_property(ContextType, const ObjectType &, uint32_t, const ValueType &);
static std::vector<String<T>> get_property_names(ContextType, const ObjectType &);
template<typename P>
static ValueType validated_get_property(ContextType ctx, const ObjectType &object, const P &property, const char *message = nullptr) {
if (!has_property(ctx, object, property)) {
throw std::out_of_range(message ?: "Object missing expected property: " + util::to_string(property));
}
return get_property(ctx, object, property);
}
static uint32_t validated_get_length(ContextType ctx, const ObjectType &object) {
static const String<T> length_string = "length";
return Value<T>::validated_to_number(ctx, get_property(ctx, object, length_string));
}
#define VALIDATED(return_t, type) \
static return_t validated_get_##type(ContextType ctx, const ObjectType &object, const String<T> &key, const char *message = nullptr) { \
try { \
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \
} \
catch (std::invalid_argument &e) { \
throw message ? std::invalid_argument(message) : e; \
} \
} \
static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \
try { \
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, index)); \
} \
catch (std::invalid_argument &e) { \
throw message ? std::invalid_argument(message) : e; \
} \
}
VALIDATED(ObjectType, array)
VALIDATED(bool, boolean)
VALIDATED(FunctionType, constructor)
VALIDATED(ObjectType, date)
VALIDATED(FunctionType, function)
VALIDATED(double, number)
VALIDATED(ObjectType, object)
VALIDATED(String<T>, string)
#undef VALIDATED
static ValueType call_method(ContextType ctx, const ObjectType &object, const String<T> &name, uint32_t argc, const ValueType arguments[]) {
FunctionType method = validated_get_function(ctx, object, name);
return Function<T>::call(ctx, method, object, argc, arguments);
}
static ValueType call_method(ContextType ctx, const ObjectType &object, const String<T> &name, const std::vector<ValueType> &arguments) {
return call_method(ctx, object, name, (uint32_t)arguments.size(), arguments.data());
}
static ObjectType create_empty(ContextType);
static ObjectType create_array(ContextType, uint32_t, const ValueType[]);
static ObjectType create_array(ContextType ctx, const std::vector<ValueType> &values) {
return create_array(ctx, (uint32_t)values.size(), values.data());
}
static ObjectType create_array(ContextType ctx) {
return create_array(ctx, 0, nullptr);
}
static ObjectType create_date(ContextType, double);
template<typename ClassType>
static ObjectType create_instance(ContextType, typename ClassType::Internal*);
template<typename ClassType>
static bool is_instance(ContextType, const ObjectType &);
template<typename ClassType>
static typename ClassType::Internal* get_internal(const ObjectType &);
template<typename ClassType>
static void set_internal(const ObjectType &, typename ClassType::Internal*);
};
template<typename ValueType>
class Protected {
operator ValueType() const;
bool operator==(const ValueType &) const;
bool operator!=(const ValueType &) const;
bool operator==(const Protected<ValueType> &) const;
bool operator!=(const Protected<ValueType> &) const;
};
template<typename T>
struct Exception : public std::runtime_error {
using ContextType = typename T::Context;
using ValueType = typename T::Value;
const Protected<ValueType> m_value;
Exception(ContextType ctx, const std::string &message)
: std::runtime_error(message), m_value(value(ctx, message)) {}
Exception(ContextType ctx, const ValueType &val)
: std::runtime_error(std::string(Value<T>::to_string(ctx, val))), m_value(ctx, val) {}
operator ValueType() const {
return m_value;
}
static ValueType value(ContextType ctx, const std::string &message);
static ValueType value(ContextType ctx, const std::exception &exp) {
if (const Exception<T> *js_exp = dynamic_cast<const Exception<T> *>(&exp)) {
return *js_exp;
}
return value(ctx, exp.what());
}
};
template<typename T>
struct ReturnValue {
using ValueType = typename T::Value;
void set(const ValueType &);
void set(const std::string &);
void set(bool);
void set(double);
void set(int32_t);
void set(uint32_t);
void set_null();
void set_undefined();
};
template<typename T, typename ClassType>
REALM_JS_INLINE typename T::Object create_object(typename T::Context ctx, typename ClassType::Internal* internal = nullptr) {
return Object<T>::template create_instance<ClassType>(ctx, internal);
}
template<typename T, typename ClassType>
REALM_JS_INLINE typename ClassType::Internal* get_internal(const typename T::Object &object) {
return Object<T>::template get_internal<ClassType>(object);
}
template<typename T, typename ClassType>
REALM_JS_INLINE void set_internal(const typename T::Object &object, typename ClassType::Internal* ptr) {
Object<T>::template set_internal<ClassType>(object, ptr);
}
} // js
} // realm

View File

@ -1,99 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#include "js_util.hpp"
#include <JavaScriptCore/JSStringRef.h>
using namespace realm;
JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp) {
JSValueRef value = exp.exception();
return JSObjectMakeError(ctx, 1, &value, NULL);
}
JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp) {
if (RJSException *rjsExp = dynamic_cast<RJSException *>(&exp)) {
return RJSMakeError(ctx, *rjsExp);
}
return RJSMakeError(ctx, exp.what());
}
JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message) {
JSValueRef value = RJSValueForString(ctx, message);
return JSObjectMakeError(ctx, 1, &value, NULL);
}
std::string RJSStringForJSString(JSStringRef jsString) {
std::string str;
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
str.resize(maxSize);
str.resize(JSStringGetUTF8CString(jsString, &str[0], maxSize) - 1);
return str;
}
std::string RJSStringForValue(JSContextRef ctx, JSValueRef value) {
JSValueRef exception = nullptr;
JSStringRef jsString = JSValueToStringCopy(ctx, value, &exception);
if (!jsString) {
throw RJSException(ctx, exception);
}
std::string string = RJSStringForJSString(jsString);
JSStringRelease(jsString);
return string;
}
std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name) {
if (!JSValueIsString(ctx, value)) {
if (name) {
throw std::invalid_argument((std::string)"'" + name + "' must be of type 'String'");
}
else {
throw std::invalid_argument("JSValue must be of type 'String'");
}
}
return RJSStringForValue(ctx, value);
}
JSStringRef RJSStringForString(const std::string &str) {
return JSStringCreateWithUTF8CString(str.c_str());
}
JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str) {
JSStringRef jsStr = RJSStringForString(str);
JSValueRef value = JSValueMakeString(ctx, jsStr);
JSStringRelease(jsStr);
return value;
}
bool RJSIsValueArray(JSContextRef ctx, JSValueRef value) {
static JSStringRef arrayString = JSStringCreateWithUTF8CString("Array");
return RJSIsValueObjectOfType(ctx, value, arrayString);
}
bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value) {
static JSStringRef arrayString = JSStringCreateWithUTF8CString("ArrayBuffer");
return RJSIsValueObjectOfType(ctx, value, arrayString);
}
bool RJSIsValueDate(JSContextRef ctx, JSValueRef value) {
static JSStringRef dateString = JSStringCreateWithUTF8CString("Date");
return RJSIsValueObjectOfType(ctx, value, dateString);
}

View File

@ -18,200 +18,25 @@
#pragma once
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <math.h>
#include <string>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include "property.hpp"
#include "schema.hpp"
#include "shared_realm.hpp"
namespace realm {
namespace js {
template<typename T>
inline void RJSFinalize(JSObjectRef object) {
delete static_cast<T>(JSObjectGetPrivate(object));
JSObjectSetPrivate(object, NULL);
class RealmDelegate;
template<typename T>
static inline RealmDelegate<T> *get_delegate(Realm *realm) {
return static_cast<RealmDelegate<T> *>(realm->m_binding_context.get());
}
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) {
static inline T stot(const std::string &s) {
std::istringstream iss(s);
T value;
iss >> value;
@ -221,27 +46,34 @@ T stot(const std::string s) {
return value;
}
static inline size_t RJSValidatedPositiveIndex(std::string indexStr) {
long index = stot<long>(indexStr);
static inline uint32_t validated_positive_index(std::string string) {
int64_t index = stot<int64_t>(string);
if (index < 0) {
throw std::out_of_range(std::string("Index ") + indexStr + " cannot be less than zero.");
throw std::out_of_range(std::string("Index ") + string + " cannot be less than zero.");
}
return index;
if (index > std::numeric_limits<uint32_t>::max()) {
throw std::out_of_range(std::string("Index ") + string + " must be a 32-bit unsigned integer");
}
return static_cast<uint32_t>(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);
static inline void validate_argument_count(size_t count, size_t expected, const char *message = nullptr) {
if (count != expected) {
throw std::invalid_argument(message ?: "Invalid arguments");
}
bool ret = JSValueIsInstanceOfConstructor(ctx, value, RJSValidatedValueToObject(ctx, constructorValue), &exception);
if (exception) {
throw RJSException(ctx, exception);
}
return ret;
}
static inline void validate_argument_count(size_t count, size_t min, size_t max, const char *message = nullptr) {
if (count < min || count > max) {
throw std::invalid_argument(message ?: "Invalid arguments");
}
}
static inline void validate_argument_count_at_least(size_t count, size_t expected, const char *message = nullptr) {
if (count < expected) {
throw std::invalid_argument(message ?: "Invalid arguments");
}
}
} // js
} // realm

426
src/jsc/jsc_class.hpp Normal file
View File

@ -0,0 +1,426 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
#include "js_class.hpp"
#include "js_util.hpp"
namespace realm {
namespace jsc {
template<typename T>
using ClassDefinition = js::ClassDefinition<Types, T>;
using ConstructorType = js::ConstructorType<Types>;
using MethodType = js::MethodType<Types>;
using PropertyType = js::PropertyType<Types>;
using IndexPropertyType = js::IndexPropertyType<Types>;
using StringPropertyType = js::StringPropertyType<Types>;
using MethodMap = js::MethodMap<Types>;
using PropertyMap = js::PropertyMap<Types>;
template<typename ClassType>
class ObjectWrap {
using Internal = typename ClassType::Internal;
using ParentClassType = typename ClassType::Parent;
public:
static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) {
return JSObjectMake(ctx, get_class(), new ObjectWrap<ClassType>(internal));
}
static JSObjectRef create_constructor(JSContextRef ctx) {
if (JSClassRef constructor_class = get_constructor_class()) {
return JSObjectMake(ctx, constructor_class, nullptr);
}
return JSObjectMakeConstructor(ctx, get_class(), construct);
}
static JSClassRef get_class() {
static JSClassRef js_class = create_class();
return js_class;
}
static JSClassRef get_constructor_class() {
static JSClassRef js_class = create_constructor_class();
return js_class;
}
static bool has_instance(JSContextRef ctx, JSValueRef value) {
return JSValueIsObjectOfClass(ctx, value, get_class());
}
operator Internal*() const {
return m_object.get();
}
ObjectWrap<ClassType>& operator=(Internal* object) {
if (m_object.get() != object) {
m_object = std::unique_ptr<Internal>(object);
}
return *this;
}
private:
static ClassType s_class;
std::unique_ptr<Internal> m_object;
ObjectWrap(Internal* object = nullptr) : m_object(object) {}
static JSClassRef create_constructor_class();
static JSClassRef create_class();
static std::vector<JSStaticFunction> get_methods(const MethodMap &);
static std::vector<JSStaticValue> get_properties(const PropertyMap &);
static JSObjectRef construct(JSContextRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);
static void finalize(JSObjectRef);
static void get_property_names(JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef);
static JSValueRef get_property(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);
static bool set_property(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);
static bool set_readonly_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) {
*exception = Exception::value(ctx, std::string("Cannot assign to read only property '") + std::string(String(property)) + "'");
return false;
}
static bool has_instance(JSContextRef ctx, JSObjectRef constructor, JSValueRef value, JSValueRef* exception) {
return JSValueIsObjectOfClass(ctx, value, get_class());
}
};
template<>
class ObjectWrap<void> {
public:
using Internal = void;
static JSClassRef get_class() {
return nullptr;
}
};
// The static class variable must be defined as well.
template<typename ClassType>
ClassType ObjectWrap<ClassType>::s_class;
template<typename ClassType>
inline JSClassRef ObjectWrap<ClassType>::create_class() {
JSClassDefinition definition = kJSClassDefinitionEmpty;
std::vector<JSStaticFunction> methods;
std::vector<JSStaticValue> properties;
definition.parentClass = ObjectWrap<ParentClassType>::get_class();
definition.className = s_class.name.c_str();
definition.finalize = finalize;
if (!s_class.methods.empty()) {
methods = get_methods(s_class.methods);
definition.staticFunctions = methods.data();
}
if (!s_class.properties.empty()) {
properties = get_properties(s_class.properties);
definition.staticValues = properties.data();
}
if (s_class.index_accessor.getter || s_class.string_accessor.getter) {
definition.getProperty = get_property;
definition.setProperty = set_property;
}
else if (s_class.index_accessor.setter || s_class.string_accessor.setter) {
definition.setProperty = set_property;
}
if (s_class.index_accessor.getter || s_class.string_accessor.enumerator) {
definition.getPropertyNames = get_property_names;
}
return JSClassCreate(&definition);
}
template<typename ClassType>
inline JSClassRef ObjectWrap<ClassType>::create_constructor_class() {
// Skip creating a special constructor class if possible.
if (!s_class.constructor && s_class.static_methods.empty() && s_class.static_properties.empty()) {
return nullptr;
}
JSClassDefinition definition = kJSClassDefinitionEmpty;
std::vector<JSStaticFunction> methods;
std::vector<JSStaticValue> properties;
definition.attributes = kJSClassAttributeNoAutomaticPrototype;
definition.className = s_class.name.c_str();
definition.hasInstance = has_instance;
if (s_class.constructor) {
definition.callAsConstructor = construct;
}
if (!s_class.static_methods.empty()) {
methods = get_methods(s_class.static_methods);
definition.staticFunctions = methods.data();
}
if (!s_class.static_properties.empty()) {
properties = get_properties(s_class.static_properties);
definition.staticValues = properties.data();
}
return JSClassCreate(&definition);
}
template<typename ClassType>
inline std::vector<JSStaticFunction> ObjectWrap<ClassType>::get_methods(const MethodMap &methods) {
std::vector<JSStaticFunction> functions;
functions.reserve(methods.size() + 1);
JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
size_t index = 0;
for (auto &pair : methods) {
functions[index++] = {pair.first.c_str(), pair.second, attributes};
}
functions[index] = {0};
return functions;
}
template<typename ClassType>
inline std::vector<JSStaticValue> ObjectWrap<ClassType>::get_properties(const PropertyMap &properties) {
std::vector<JSStaticValue> values;
values.reserve(properties.size() + 1);
JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
size_t index = 0;
for (auto &pair : properties) {
auto &prop = pair.second;
values[index++] = {pair.first.c_str(), prop.getter, prop.setter ?: set_readonly_property, attributes};
}
values[index] = {0};
return values;
}
template<typename ClassType>
inline JSObjectRef ObjectWrap<ClassType>::construct(JSContextRef ctx, JSObjectRef constructor, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
if (!s_class.constructor) {
*exception = jsc::Exception::value(ctx, "Illegal constructor");
return nullptr;
}
JSObjectRef this_object = ObjectWrap<ClassType>::create_instance(ctx);
try {
s_class.constructor(ctx, this_object, argc, arguments);
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
}
return this_object;
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::finalize(JSObjectRef object) {
// This is called for the most derived class before superclasses.
if (auto wrap = static_cast<ObjectWrap<ClassType> *>(JSObjectGetPrivate(object))) {
delete wrap;
JSObjectSetPrivate(object, nullptr);
}
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::get_property_names(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) {
if (s_class.index_accessor.getter) {
try {
uint32_t length = Object::validated_get_length(ctx, object);
char string[32];
for (uint32_t i = 0; i < length; i++) {
sprintf(string, "%u", i);
JSPropertyNameAccumulatorAddName(accumulator, jsc::String(string));
}
}
catch (std::exception &) {
// Enumerating properties should never throw an exception into JS.
}
}
if (auto string_enumerator = s_class.string_accessor.enumerator) {
string_enumerator(ctx, object, accumulator);
}
}
template<typename ClassType>
inline JSValueRef ObjectWrap<ClassType>::get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
if (auto index_getter = s_class.index_accessor.getter) {
try {
uint32_t index = validated_positive_index(jsc::String(property));
return index_getter(ctx, object, index, exception);
}
catch (std::out_of_range &) {
// Out-of-bounds index getters should just return undefined in JS.
return Value::from_undefined(ctx);
}
catch (std::invalid_argument &) {
// Property is not a number.
}
}
if (auto string_getter = s_class.string_accessor.getter) {
return string_getter(ctx, object, property, exception);
}
return nullptr;
}
template<typename ClassType>
inline bool ObjectWrap<ClassType>::set_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) {
auto index_setter = s_class.index_accessor.setter;
if (index_setter || s_class.index_accessor.getter) {
try {
uint32_t index = validated_positive_index(jsc::String(property));
if (index_setter) {
return index_setter(ctx, object, index, value, exception);
}
else {
*exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index));
return false;
}
}
catch (std::out_of_range &e) {
*exception = Exception::value(ctx, e);
return false;
}
catch (std::invalid_argument &) {
// Property is not a number.
}
}
if (auto string_setter = s_class.string_accessor.setter) {
return string_setter(ctx, object, property, value, exception);
}
return false;
}
} // jsc
namespace js {
template<typename ClassType>
class ObjectWrap<jsc::Types, ClassType> : public jsc::ObjectWrap<ClassType> {};
template<jsc::MethodType F>
JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
jsc::ReturnValue return_value(ctx);
try {
F(ctx, this_object, argc, arguments, return_value);
return return_value;
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return nullptr;
}
}
template<jsc::PropertyType::GetterType F>
JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
jsc::ReturnValue return_value(ctx);
try {
F(ctx, object, return_value);
return return_value;
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return nullptr;
}
}
template<jsc::PropertyType::SetterType F>
bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) {
try {
F(ctx, object, value);
return true;
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return false;
}
}
template<jsc::IndexPropertyType::GetterType F>
JSValueRef wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef* exception) {
jsc::ReturnValue return_value(ctx);
try {
F(ctx, object, index, return_value);
return return_value;
}
catch (std::out_of_range &) {
// Out-of-bounds index getters should just return undefined in JS.
return jsc::Value::from_undefined(ctx);
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return nullptr;
}
}
template<jsc::IndexPropertyType::SetterType F>
bool wrap(JSContextRef ctx, JSObjectRef object, uint32_t index, JSValueRef value, JSValueRef* exception) {
try {
return F(ctx, object, index, value);
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return false;
}
}
template<jsc::StringPropertyType::GetterType F>
JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
jsc::ReturnValue return_value(ctx);
try {
F(ctx, object, property, return_value);
return return_value;
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return nullptr;
}
}
template<jsc::StringPropertyType::SetterType F>
bool wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef* exception) {
try {
return F(ctx, object, property, value);
}
catch (std::exception &e) {
*exception = jsc::Exception::value(ctx, e);
return false;
}
}
template<jsc::StringPropertyType::EnumeratorType F>
void wrap(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator) {
auto names = F(ctx, object);
for (auto &name : names) {
JSPropertyNameAccumulatorAddName(accumulator, name);
}
}
} // js
} // realm

View File

@ -18,11 +18,15 @@
#pragma once
#include "js_util.hpp"
#include "jsc_types.hpp"
namespace realm {
class Object;
namespace js {
template<>
inline JSGlobalContextRef jsc::Context::get_global_context(JSContextRef ctx) {
return JSContextGetGlobalContext(ctx);
}
JSClassRef RJSObjectClass();
JSObjectRef RJSObjectCreate(JSContextRef ctx, realm::Object object);
} // js
} // realm

View File

@ -16,15 +16,18 @@
//
////////////////////////////////////////////////////////////////////////////
#include "js_collection.hpp"
#pragma once
static JSClassRef RJSCreateCollectionClass() {
JSClassDefinition classDefinition = kJSClassDefinitionEmpty;
classDefinition.className = "Collection";
return JSClassCreate(&classDefinition);
}
#include "jsc_types.hpp"
JSClassRef RJSCollectionClass() {
static JSClassRef s_collectionClass = RJSCreateCollectionClass();
return s_collectionClass;
namespace realm {
namespace js {
template<>
inline JSValueRef jsc::Exception::value(JSContextRef ctx, const std::string &message) {
JSValueRef value = jsc::Value::from_string(ctx, message);
return JSObjectMakeError(ctx, 1, &value, NULL);
}
} // js
} // realm

47
src/jsc/jsc_function.hpp Normal file
View File

@ -0,0 +1,47 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
template<>
inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef &function, const JSObjectRef &this_object, size_t argc, const JSValueRef arguments[]) {
JSValueRef exception = nullptr;
JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return result;
}
template<>
inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef &function, size_t argc, const JSValueRef arguments[]) {
JSValueRef exception = nullptr;
JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return result;
}
} // js
} // realm

43
src/jsc/jsc_init.cpp Normal file
View File

@ -0,0 +1,43 @@
////////////////////////////////////////////////////////////////////////////
//
// 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 <algorithm>
#include <cassert>
#include "jsc_init.hpp"
#include "platform.hpp"
extern "C" {
using namespace realm;
using namespace realm::jsc;
JSObjectRef RJSConstructorCreate(JSContextRef ctx) {
return js::Realm<Types>::create_constructor(ctx);
}
void RJSInitializeInContext(JSContextRef ctx) {
static const String realm_string = "Realm";
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
JSObjectRef realm_constructor = RJSConstructorCreate(ctx);
jsc::Object::set_property(ctx, global_object, realm_string, realm_constructor, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete));
}
} // extern "C"

32
src/jsc/jsc_init.hpp Normal file
View File

@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_init.h"
#include "jsc_string.hpp"
#include "jsc_protected.hpp"
#include "jsc_context.hpp"
#include "jsc_value.hpp"
#include "jsc_object.hpp"
#include "jsc_function.hpp"
#include "jsc_exception.hpp"
#include "jsc_return_value.hpp"
#include "jsc_object_accessor.hpp"
#include "js_realm.hpp"

147
src/jsc/jsc_object.hpp Normal file
View File

@ -0,0 +1,147 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
template<>
inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) {
return JSObjectHasProperty(ctx, object, key);
}
template<>
inline bool jsc::Object::has_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) {
return JSObjectHasProperty(ctx, object, jsc::String(util::to_string(index)));
}
template<>
inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key) {
JSValueRef exception = nullptr;
JSValueRef value = JSObjectGetProperty(ctx, object, key, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return value;
}
template<>
inline JSValueRef jsc::Object::get_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index) {
JSValueRef exception = nullptr;
JSValueRef value = JSObjectGetPropertyAtIndex(ctx, object, index, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return value;
}
template<>
inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, const jsc::String &key, const JSValueRef &value, PropertyAttributes attributes) {
JSValueRef exception = nullptr;
JSObjectSetProperty(ctx, object, key, value, attributes << 1, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
}
template<>
inline void jsc::Object::set_property(JSContextRef ctx, const JSObjectRef &object, uint32_t index, const JSValueRef &value) {
JSValueRef exception = nullptr;
JSObjectSetPropertyAtIndex(ctx, object, index, value, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
}
template<>
inline std::vector<jsc::String> jsc::Object::get_property_names(JSContextRef ctx, const JSObjectRef &object) {
JSPropertyNameArrayRef property_names = JSObjectCopyPropertyNames(ctx, object);
size_t property_count = JSPropertyNameArrayGetCount(property_names);
std::vector<jsc::String> names;
names.reserve(property_count);
for (size_t i = 0; i < property_count; i++) {
names.push_back(JSPropertyNameArrayGetNameAtIndex(property_names, i));
}
JSPropertyNameArrayRelease(property_names);
return names;
}
template<>
inline JSValueRef jsc::Object::get_prototype(JSContextRef ctx, const JSObjectRef &object) {
return JSObjectGetPrototype(ctx, object);
}
template<>
inline void jsc::Object::set_prototype(JSContextRef ctx, const JSObjectRef &object, const JSValueRef &prototype) {
JSObjectSetPrototype(ctx, object, prototype);
}
template<>
inline JSObjectRef jsc::Object::create_empty(JSContextRef ctx) {
return JSObjectMake(ctx, nullptr, nullptr);
}
template<>
inline JSObjectRef jsc::Object::create_array(JSContextRef ctx, uint32_t length, const JSValueRef values[]) {
JSValueRef exception = nullptr;
JSObjectRef array = JSObjectMakeArray(ctx, length, values, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return array;
}
template<>
inline JSObjectRef jsc::Object::create_date(JSContextRef ctx, double time) {
JSValueRef number = jsc::Value::from_number(ctx, time);
return JSObjectMakeDate(ctx, 1, &number, nullptr);
}
template<>
template<typename ClassType>
inline JSObjectRef jsc::Object::create_instance(JSContextRef ctx, typename ClassType::Internal* internal) {
return jsc::ObjectWrap<ClassType>::create_instance(ctx, internal);
}
template<>
template<typename ClassType>
inline bool jsc::Object::is_instance(JSContextRef ctx, const JSObjectRef &object) {
return jsc::ObjectWrap<ClassType>::has_instance(ctx, object);
}
template<>
template<typename ClassType>
inline typename ClassType::Internal* jsc::Object::get_internal(const JSObjectRef &object) {
return *static_cast<jsc::ObjectWrap<ClassType> *>(JSObjectGetPrivate(object));
}
template<>
template<typename ClassType>
inline void jsc::Object::set_internal(const JSObjectRef &object, typename ClassType::Internal* ptr) {
auto wrap = static_cast<jsc::ObjectWrap<ClassType> *>(JSObjectGetPrivate(object));
*wrap = ptr;
}
} // js
} // realm

View File

@ -0,0 +1,99 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_class.hpp"
#include "js_object_accessor.hpp"
namespace realm {
// Specialize a native accessor class for JSC.
template<>
class NativeAccessor<jsc::Types::Value, jsc::Types::Context> : public js::NativeAccessor<jsc::Types> {};
namespace js {
template<>
inline std::string NativeAccessor<jsc::Types>::to_binary(JSContextRef ctx, JSValueRef &value) {
static jsc::String s_array_buffer = "ArrayBuffer";
static jsc::String s_buffer = "buffer";
static jsc::String s_byte_length = "byteLength";
static jsc::String s_byte_offset = "byteOffset";
static jsc::String s_is_view = "isView";
static jsc::String s_uint8_array = "Uint8Array";
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer);
JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_uint8_array);
JSValueRef uint8_array_arguments[3];
uint32_t uint8_array_argc = 0;
// Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView).
if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) {
uint8_array_arguments[0] = value;
uint8_array_argc = 1;
}
else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) {
// Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val).
JSValueRef is_view = jsc::Object::call_method(ctx, array_buffer_constructor, s_is_view, 1, &object);
if (jsc::Value::to_boolean(ctx, is_view)) {
uint8_array_arguments[0] = jsc::Object::validated_get_object(ctx, object, s_buffer);
uint8_array_arguments[1] = jsc::Object::get_property(ctx, object, s_byte_offset);
uint8_array_arguments[2] = jsc::Object::get_property(ctx, object, s_byte_length);
uint8_array_argc = 3;
}
}
if (!uint8_array_argc) {
throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary");
}
JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments);
uint32_t byte_count = jsc::Object::validated_get_length(ctx, uint8_array);
std::string bytes(byte_count, 0);
for (uint32_t i = 0; i < byte_count; i++) {
JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i);
bytes[i] = jsc::Value::to_number(ctx, byteValue);
}
return bytes;
}
template<>
inline JSValueRef NativeAccessor<jsc::Types>::from_binary(JSContextRef ctx, BinaryData data) {
static jsc::String s_buffer = "buffer";
static jsc::String s_uint8_array = "Uint8Array";
size_t byte_count = data.size();
JSValueRef byte_count_value = jsc::Value::from_number(ctx, byte_count);
JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(ctx, JSContextGetGlobalObject(ctx), s_uint8_array);
JSObjectRef uint8_array = jsc::Function::construct(ctx, uint8_array_constructor, 1, &byte_count_value);
for (uint32_t i = 0; i < byte_count; i++) {
JSValueRef num = jsc::Value::from_number(ctx, data[i]);
jsc::Object::set_property(ctx, uint8_array, i, num);
}
return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer);
}
} // js
} // realm

86
src/jsc/jsc_protected.hpp Normal file
View File

@ -0,0 +1,86 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
template<>
class Protected<JSGlobalContextRef> {
JSGlobalContextRef m_context;
public:
Protected(const Protected<JSGlobalContextRef> &other) : Protected(other.m_context) {}
Protected(Protected<JSGlobalContextRef> &&other) : m_context(other.m_context) {
other.m_context = nullptr;
}
explicit Protected(JSGlobalContextRef ctx) : m_context(ctx) {
JSGlobalContextRetain(m_context);
}
~Protected() {
if (m_context) {
JSGlobalContextRelease(m_context);
}
}
operator JSGlobalContextRef() const {
return m_context;
}
};
template<>
class Protected<JSValueRef> {
JSGlobalContextRef m_context;
JSValueRef m_value;
public:
Protected(const Protected<JSValueRef> &other) : Protected(other.m_context, other.m_value) {}
Protected(Protected<JSValueRef> &&other) : m_context(other.m_context), m_value(other.m_value) {
other.m_context = nullptr;
other.m_value = nullptr;
}
Protected(JSContextRef ctx, JSValueRef value) : m_context(JSContextGetGlobalContext(ctx)), m_value(value) {
JSValueProtect(m_context, m_value);
}
~Protected() {
if (m_value) {
JSValueUnprotect(m_context, m_value);
}
}
operator JSValueRef() const {
return m_value;
}
};
template<>
class Protected<JSObjectRef> : public Protected<JSValueRef> {
public:
Protected(const Protected<JSObjectRef> &other) : Protected<JSValueRef>(other) {}
Protected(Protected<JSObjectRef> &&other) : Protected<JSValueRef>(std::move(other)) {}
Protected(JSContextRef ctx, JSObjectRef value) : Protected<JSValueRef>(ctx, value) {}
operator JSObjectRef() const {
JSValueRef value = static_cast<JSValueRef>(*this);
return (JSObjectRef)value;
}
};
} // js
} // realm

View File

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
template<>
class ReturnValue<jsc::Types> {
const JSContextRef m_context;
JSValueRef m_value = nullptr;
public:
ReturnValue(JSContextRef ctx) : m_context(ctx) {}
void set(const JSValueRef &value) {
m_value = value;
}
void set(const std::string &string) {
m_value = JSValueMakeString(m_context, jsc::String(string));
}
void set(bool boolean) {
m_value = JSValueMakeBoolean(m_context, boolean);
}
void set(double number) {
m_value = JSValueMakeNumber(m_context, number);
}
void set(int32_t number) {
m_value = JSValueMakeNumber(m_context, number);
}
void set(uint32_t number) {
m_value = JSValueMakeNumber(m_context, number);
}
void set_null() {
m_value = JSValueMakeNull(m_context);
}
void set_undefined() {
m_value = JSValueMakeUndefined(m_context);
}
operator JSValueRef() const {
return m_value;
}
};
} // js
} // realm

59
src/jsc/jsc_string.hpp Normal file
View File

@ -0,0 +1,59 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
template<>
class String<jsc::Types> {
using StringType = String<jsc::Types>;
JSStringRef m_str;
public:
String(const char *s) : m_str(JSStringCreateWithUTF8CString(s)) {}
String(const JSStringRef &s) : m_str(JSStringRetain(s)) {}
String(const std::string &str) : String(str.c_str()) {}
String(const StringType &o) : String(o.m_str) {}
String(StringType &&o) : m_str(o.m_str) {
o.m_str = nullptr;
}
~String() {
if (m_str) {
JSStringRelease(m_str);
}
}
operator JSStringRef() const {
return m_str;
}
operator std::string() const {
size_t max_size = JSStringGetMaximumUTF8CStringSize(m_str);
std::string string;
string.resize(max_size);
string.resize(JSStringGetUTF8CString(m_str, &string[0], max_size) - 1);
return string;
}
};
} // js
} // realm

62
src/jsc/jsc_types.hpp Normal file
View File

@ -0,0 +1,62 @@
////////////////////////////////////////////////////////////////////////////
//
// 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 "js_types.hpp"
namespace realm {
namespace jsc {
struct Types {
using Context = JSContextRef;
using GlobalContext = JSGlobalContextRef;
using ClassDefinition = JSClassRef;
using Value = JSValueRef;
using Object = JSObjectRef;
using String = JSStringRef;
using Function = JSObjectRef;
using ConstructorCallback = JSObjectCallAsConstructorCallback;
using FunctionCallback = JSObjectCallAsFunctionCallback;
using PropertyGetterCallback = JSObjectGetPropertyCallback;
using PropertySetterCallback = JSObjectSetPropertyCallback;
using IndexPropertyGetterCallback = JSValueRef (*)(JSContextRef, JSObjectRef, uint32_t, JSValueRef*);
using IndexPropertySetterCallback = bool (*)(JSContextRef, JSObjectRef, uint32_t, JSValueRef, JSValueRef*);
using StringPropertyGetterCallback = JSObjectGetPropertyCallback;
using StringPropertySetterCallback = JSObjectSetPropertyCallback;
using StringPropertyEnumeratorCallback = JSObjectGetPropertyNamesCallback;
};
template<typename ClassType>
class ObjectWrap;
using String = js::String<Types>;
using Context = js::Context<Types>;
using Value = js::Value<Types>;
using Function = js::Function<Types>;
using Object = js::Object<Types>;
using Exception = js::Exception<Types>;
using ReturnValue = js::ReturnValue<Types>;
} // jsc
} // realm

194
src/jsc/jsc_value.hpp Normal file
View File

@ -0,0 +1,194 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "jsc_types.hpp"
namespace realm {
namespace js {
static inline bool is_object_of_type(JSContextRef ctx, JSValueRef value, jsc::String type) {
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
JSValueRef exception = nullptr;
JSValueRef constructor = JSObjectGetProperty(ctx, global_object, type, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
bool result = JSValueIsInstanceOfConstructor(ctx, value, jsc::Value::validated_to_constructor(ctx, constructor), &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return result;
}
template<>
inline bool jsc::Value::is_array(JSContextRef ctx, const JSValueRef &value) {
// JSValueIsArray() is not available until iOS 9.
static const jsc::String type = "Array";
return is_object_of_type(ctx, value, type);
}
template<>
inline bool jsc::Value::is_array_buffer(JSContextRef ctx, const JSValueRef &value) {
static const jsc::String type = "ArrayBuffer";
return is_object_of_type(ctx, value, type);
}
template<>
inline bool jsc::Value::is_date(JSContextRef ctx, const JSValueRef &value) {
static const jsc::String type = "Date";
return is_object_of_type(ctx, value, type);
}
template<>
inline bool jsc::Value::is_boolean(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsBoolean(ctx, value);
}
template<>
inline bool jsc::Value::is_constructor(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value);
}
template<>
inline bool jsc::Value::is_function(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsObject(ctx, value) && JSObjectIsFunction(ctx, (JSObjectRef)value);
}
template<>
inline bool jsc::Value::is_null(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsNull(ctx, value);
}
template<>
inline bool jsc::Value::is_number(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsNumber(ctx, value);
}
template<>
inline bool jsc::Value::is_object(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsObject(ctx, value);
}
template<>
inline bool jsc::Value::is_string(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsString(ctx, value);
}
template<>
inline bool jsc::Value::is_undefined(JSContextRef ctx, const JSValueRef &value) {
return JSValueIsUndefined(ctx, value);
}
template<>
inline bool jsc::Value::is_valid(const JSValueRef &value) {
return value != nullptr;
}
template<>
inline JSValueRef jsc::Value::from_boolean(JSContextRef ctx, bool boolean) {
return JSValueMakeBoolean(ctx, boolean);
}
template<>
inline JSValueRef jsc::Value::from_null(JSContextRef ctx) {
return JSValueMakeNull(ctx);
}
template<>
inline JSValueRef jsc::Value::from_number(JSContextRef ctx, double number) {
return JSValueMakeNumber(ctx, number);
}
template<>
inline JSValueRef jsc::Value::from_string(JSContextRef ctx, const jsc::String &string) {
return JSValueMakeString(ctx, string);
}
template<>
inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) {
return JSValueMakeUndefined(ctx);
}
template<>
inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) {
return JSValueToBoolean(ctx, value);
}
template<>
inline double jsc::Value::to_number(JSContextRef ctx, const JSValueRef &value) {
JSValueRef exception = nullptr;
double number = JSValueToNumber(ctx, value, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
if (isnan(number)) {
throw std::invalid_argument("Value not convertible to a number.");
}
return number;
}
template<>
inline jsc::String jsc::Value::to_string(JSContextRef ctx, const JSValueRef &value) {
JSValueRef exception = nullptr;
jsc::String string = JSValueToStringCopy(ctx, value, &exception);
// Since the string's retain value is +2 here, we need to manually release it before returning.
JSStringRelease(string);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return string;
}
template<>
inline JSObjectRef jsc::Value::to_object(JSContextRef ctx, const JSValueRef &value) {
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(ctx, value, &exception);
if (exception) {
throw jsc::Exception(ctx, exception);
}
return object;
}
template<>
inline JSObjectRef jsc::Value::to_array(JSContextRef ctx, const JSValueRef &value) {
return to_object(ctx, value);
}
template<>
inline JSObjectRef jsc::Value::to_constructor(JSContextRef ctx, const JSValueRef &value) {
return to_object(ctx, value);
}
template<>
inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) {
return to_object(ctx, value);
}
template<>
inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &value) {
return to_object(ctx, value);
}
} // js
} // realm

54
src/node/binding.gyp Normal file
View File

@ -0,0 +1,54 @@
{
"targets": [
{
"target_name": "realm",
"sources": [
"node_init.cpp",
"../js_realm.cpp",
"../ios/platform.mm",
"../object-store/src/index_set.cpp",
"../object-store/src/list.cpp",
"../object-store/src/object_schema.cpp",
"../object-store/src/object_store.cpp",
"../object-store/src/results.cpp",
"../object-store/src/schema.cpp",
"../object-store/src/shared_realm.cpp",
"../object-store/src/impl/async_query.cpp",
"../object-store/src/impl/transact_log_handler.cpp",
"../object-store/src/impl/realm_coordinator.cpp",
"../object-store/src/impl/apple/external_commit_helper.cpp",
"../object-store/src/impl/apple/weak_realm_notifier.cpp",
"../object-store/src/parser/parser.cpp",
"../object-store/src/parser/query_builder.cpp"
],
"include_dirs": [
"..",
"../object-store/src",
"../object-store/src/impl",
"../object-store/src/impl/apple",
"../object-store/src/parser",
"../object-store/external/pegtl",
"../../core/include",
"../../node_modules/nan"
],
"library_dirs": [
"$(srcdir)/../../core"
],
"defines": ["REALM_HAVE_CONFIG"],
"cflags_cc": ["-fexceptions", "-frtti", "-std=c++14"],
"ldflags": ["-lrealm"],
"xcode_settings": {
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "10.8",
"OTHER_CPLUSPLUSFLAGS": ["-fexceptions", "-frtti"],
"OTHER_LDFLAGS": ["-lrealm", "-framework", "Foundation"]
},
"configurations": {
"Debug": {
"defines": ["DEBUG=1"]
}
}
}
]
}

404
src/node/node_class.hpp Normal file
View File

@ -0,0 +1,404 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
#include "js_class.hpp"
#include "js_util.hpp"
namespace realm {
namespace node {
template<typename T>
using ClassDefinition = js::ClassDefinition<Types, T>;
using ConstructorType = js::ConstructorType<Types>;
using MethodType = js::MethodType<Types>;
using PropertyType = js::PropertyType<Types>;
using IndexPropertyType = js::IndexPropertyType<Types>;
using StringPropertyType = js::StringPropertyType<Types>;
template<typename ClassType>
class ObjectWrap : public Nan::ObjectWrap {
using Internal = typename ClassType::Internal;
using ParentClassType = typename ClassType::Parent;
public:
static v8::Local<v8::Function> create_constructor(v8::Isolate*);
static v8::Local<v8::Object> create_instance(v8::Isolate*, Internal* = nullptr);
static v8::Local<v8::FunctionTemplate> get_template() {
static Nan::Persistent<v8::FunctionTemplate> js_template(create_template());
return Nan::New(js_template);
}
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return get_template()->HasInstance(value);
}
operator Internal*() const {
return m_object.get();
}
ObjectWrap<ClassType>& operator=(Internal* object) {
if (m_object.get() != object) {
m_object = std::unique_ptr<Internal>(object);
}
return *this;
}
private:
static ClassType s_class;
std::unique_ptr<Internal> m_object;
ObjectWrap(Internal* object = nullptr) : m_object(object) {}
static v8::Local<v8::FunctionTemplate> create_template();
static void setup_method(v8::Local<v8::FunctionTemplate>, const std::string &, Nan::FunctionCallback);
static void setup_static_method(v8::Local<v8::FunctionTemplate>, const std::string &, Nan::FunctionCallback);
template<typename TargetType>
static void setup_property(v8::Local<TargetType>, const std::string &, const PropertyType &);
static void construct(Nan::NAN_METHOD_ARGS_TYPE);
static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE);
static void set_property(v8::Local<v8::String>, v8::Local<v8::Value>, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE);
static void set_readonly_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_SETTER_ARGS_TYPE info) {
std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'";
Nan::ThrowError(message.c_str());
}
static void set_readonly_index(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
std::string message = std::string("Cannot assign to read only index ") + util::to_string(index);
Nan::ThrowError(message.c_str());
}
static void get_nonexistent_property(v8::Local<v8::String>, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE) {
// Do nothing. This function exists only to prevent a crash where it is used.
}
};
template<>
class ObjectWrap<void> {
public:
using Internal = void;
static v8::Local<v8::FunctionTemplate> get_template() {
return v8::Local<v8::FunctionTemplate>();;
}
};
// This helper function is needed outside the scope of the ObjectWrap class as well.
static inline std::vector<v8::Local<v8::Value>> get_arguments(const Nan::FunctionCallbackInfo<v8::Value> &info) {
int count = info.Length();
std::vector<v8::Local<v8::Value>> arguments;
arguments.reserve(count);
for (int i = 0; i < count; i++) {
arguments.push_back(info[i]);
}
return arguments;
}
// The static class variable must be defined as well.
template<typename ClassType>
ClassType ObjectWrap<ClassType>::s_class;
template<typename ClassType>
inline v8::Local<v8::Function> ObjectWrap<ClassType>::create_constructor(v8::Isolate* isolate) {
Nan::EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> tpl = get_template();
v8::Local<v8::Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
for (auto &pair : s_class.static_properties) {
setup_property<v8::Object>(constructor, pair.first, pair.second);
}
return scope.Escape(constructor);
}
template<typename ClassType>
inline v8::Local<v8::Object> ObjectWrap<ClassType>::create_instance(v8::Isolate* isolate, Internal* internal) {
Nan::EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> tpl = get_template();
v8::Local<v8::Object> instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked();
auto wrap = new ObjectWrap<ClassType>(internal);
wrap->Wrap(instance);
return scope.Escape(instance);
}
template<typename ClassType>
inline v8::Local<v8::FunctionTemplate> ObjectWrap<ClassType>::create_template() {
Nan::EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(construct);
v8::Local<v8::ObjectTemplate> instance_tpl = tpl->InstanceTemplate();
v8::Local<v8::String> name = Nan::New(s_class.name).ToLocalChecked();
tpl->SetClassName(name);
instance_tpl->SetInternalFieldCount(1);
v8::Local<v8::FunctionTemplate> super_tpl = ObjectWrap<ParentClassType>::get_template();
if (!super_tpl.IsEmpty()) {
tpl->Inherit(super_tpl);
}
// Static properties are setup in create_constructor() because V8.
for (auto &pair : s_class.static_methods) {
setup_static_method(tpl, pair.first, pair.second);
}
for (auto &pair : s_class.methods) {
setup_method(tpl, pair.first, pair.second);
}
for (auto &pair : s_class.properties) {
setup_property<v8::ObjectTemplate>(instance_tpl, pair.first, pair.second);
}
if (s_class.index_accessor.getter) {
auto &index_accessor = s_class.index_accessor;
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
}
if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) {
// Use our own wrapper for the setter since we want to throw for negative indices.
auto &string_accessor = s_class.string_accessor;
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
}
return scope.Escape(tpl);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
v8::Local<v8::Signature> signature = Nan::New<v8::Signature>(tpl);
v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(callback, v8::Local<v8::Value>(), signature);
v8::Local<v8::Function> fn = Nan::GetFunction(t).ToLocalChecked();
v8::Local<v8::String> fn_name = Nan::New(name).ToLocalChecked();
// The reason we use this rather than Nan::SetPrototypeMethod is DontEnum.
tpl->PrototypeTemplate()->Set(fn_name, fn, v8::PropertyAttribute::DontEnum);
fn->SetName(fn_name);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::setup_static_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
v8::Local<v8::Function> fn = Nan::GetFunction(Nan::New<v8::FunctionTemplate>(callback)).ToLocalChecked();
v8::Local<v8::String> fn_name = Nan::New(name).ToLocalChecked();
tpl->Set(fn_name, fn, v8::PropertyAttribute::DontEnum);
fn->SetName(fn_name);
}
template<typename ClassType>
template<typename TargetType>
inline void ObjectWrap<ClassType>::setup_property(v8::Local<TargetType> target, const std::string &name, const PropertyType &property) {
v8::Local<v8::String> prop_name = Nan::New(name).ToLocalChecked();
v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete);
Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::construct(Nan::NAN_METHOD_ARGS_TYPE info) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Constructor must be called with new");
}
if (s_class.constructor) {
auto isolate = info.GetIsolate();
auto arguments = get_arguments(info);
v8::Local<v8::Object> this_object = info.This();
info.GetReturnValue().Set(this_object);
auto wrap = new ObjectWrap<ClassType>();
wrap->Wrap(this_object);
try {
s_class.constructor(isolate, this_object, arguments.size(), arguments.data());
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
else {
Nan::ThrowError("Illegal constructor");
}
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) {
uint32_t length;
try {
length = Object::validated_get_length(info.GetIsolate(), info.This());
}
catch (std::exception &) {
// Enumerating properties should never throw an exception.
return;
}
v8::Local<v8::Array> array = Nan::New<v8::Array>(length);
for (uint32_t i = 0; i < length; i++) {
Nan::Set(array, i, Nan::New(i));
}
info.GetReturnValue().Set(array);
}
template<typename ClassType>
inline void ObjectWrap<ClassType>::set_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
if (s_class.index_accessor.getter || s_class.index_accessor.setter) {
try {
// Negative indices are passed into this string property interceptor, so check for them here.
validated_positive_index(node::String(property));
}
catch (std::out_of_range &e) {
Nan::ThrowError(Exception::value(info.GetIsolate(), e));
return;
}
catch (std::invalid_argument &) {
// Property is not a number.
}
}
if (auto string_setter = s_class.string_accessor.setter) {
string_setter(property, value, info);
}
}
} // node
namespace js {
template<typename ClassType>
class ObjectWrap<node::Types, ClassType> : public node::ObjectWrap<ClassType> {};
template<node::MethodType F>
void wrap(Nan::NAN_METHOD_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
auto arguments = node::get_arguments(info);
try {
F(isolate, info.This(), arguments.size(), arguments.data(), return_value);
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::PropertyType::GetterType F>
void wrap(v8::Local<v8::String> property, Nan::NAN_GETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
F(isolate, info.This(), return_value);
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::PropertyType::SetterType F>
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_SETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
try {
F(isolate, info.This(), value);
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::IndexPropertyType::GetterType F>
void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
F(isolate, info.This(), index, return_value);
}
catch (std::out_of_range &) {
// Out-of-bounds index getters should just return undefined in JS.
return_value.set_undefined();
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::IndexPropertyType::SetterType F>
void wrap(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
try {
if (F(isolate, info.This(), index, value)) {
// Indicate that the property was intercepted.
info.GetReturnValue().Set(value);
}
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::StringPropertyType::GetterType F>
void wrap(v8::Local<v8::String> property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
node::ReturnValue return_value(info.GetReturnValue());
try {
F(isolate, info.This(), property, return_value);
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::StringPropertyType::SetterType F>
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
v8::Isolate* isolate = info.GetIsolate();
try {
if (F(isolate, info.This(), property, value)) {
// Indicate that the property was intercepted.
info.GetReturnValue().Set(value);
}
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
}
template<node::StringPropertyType::EnumeratorType F>
void wrap(Nan::NAN_PROPERTY_ENUMERATOR_ARGS_TYPE info) {
auto names = F(info.GetIsolate(), info.This());
int count = (int)names.size();
v8::Local<v8::Array> array = Nan::New<v8::Array>(count);
for (int i = 0; i < count; i++) {
Nan::Set(array, i, v8::Local<v8::String>(names[i]));
}
info.GetReturnValue().Set(array);
}
} // js
} // realm

32
src/node/node_context.hpp Normal file
View File

@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
inline v8::Local<v8::Context> node::Context::get_global_context(v8::Isolate* isolate) {
return isolate->GetCurrentContext();
}
} // js
} // realm

30
src/node/node_dummy.cpp Normal file
View File

@ -0,0 +1,30 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
// NOTE: This dummy file exists only to make Xcode build the Realm Node dynamic library.
#include "node.h"
extern "C" void node_module_register(void* mod) {}
namespace node {
namespace Buffer {
bool HasInstance(v8::Local<v8::Value> val) { return false; }
char* Data(v8::Local<v8::Value> val) { return nullptr; }
size_t Length(v8::Local<v8::Value> val) { return 0; }
}
}

View File

@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
inline v8::Local<v8::Value> node::Exception::value(v8::Isolate* isolate, const std::string &message) {
return Nan::Error(message.c_str());
}
} // js
} // realm

View File

@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
inline v8::Local<v8::Value> node::Function::call(v8::Isolate* isolate, const v8::Local<v8::Function> &function, const v8::Local<v8::Object> &this_object, size_t argc, const v8::Local<v8::Value> arguments[]) {
Nan::TryCatch trycatch;
auto result = Nan::Call(function, this_object, (int)argc, const_cast<v8::Local<v8::Value>*>(arguments));
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
return result.ToLocalChecked();
}
template<>
inline v8::Local<v8::Object> node::Function::construct(v8::Isolate* isolate, const v8::Local<v8::Function> &function, size_t argc, const v8::Local<v8::Value> arguments[]) {
Nan::TryCatch trycatch;
auto result = Nan::NewInstance(function, (int)argc, const_cast<v8::Local<v8::Value>*>(arguments));
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
return result.ToLocalChecked();
}
} // js
} // realm

34
src/node/node_init.cpp Normal file
View File

@ -0,0 +1,34 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#include "node_init.hpp"
namespace realm {
namespace node {
static void init(v8::Local<v8::Object> exports) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Function> realm_constructor = js::Realm<Types>::create_constructor(isolate);
Nan::Set(exports, realm_constructor->GetName(), realm_constructor);
}
} // node
} // realm
NODE_MODULE(Realm, realm::node::init);

31
src/node/node_init.hpp Normal file
View File

@ -0,0 +1,31 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_string.hpp"
#include "node_protected.hpp"
#include "node_context.hpp"
#include "node_value.hpp"
#include "node_object.hpp"
#include "node_function.hpp"
#include "node_exception.hpp"
#include "node_return_value.hpp"
#include "node_object_accessor.hpp"
#include "js_realm.hpp"

159
src/node/node_object.hpp Normal file
View File

@ -0,0 +1,159 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, const node::String &key) {
return Nan::Has(object, key).FromMaybe(false);
}
template<>
inline bool node::Object::has_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, uint32_t index) {
return Nan::Has(object, index).FromMaybe(false);
}
template<>
inline v8::Local<v8::Value> node::Object::get_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, const node::String &key) {
Nan::TryCatch trycatch;
auto value = Nan::Get(object, v8::Local<v8::String>(key));
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
return value.ToLocalChecked();
}
template<>
inline v8::Local<v8::Value> node::Object::get_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, uint32_t index) {
Nan::TryCatch trycatch;
auto value = Nan::Get(object, index);
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
return value.ToLocalChecked();
}
template<>
inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, const node::String &key, const v8::Local<v8::Value> &value, PropertyAttributes attributes) {
Nan::TryCatch trycatch;
if (attributes) {
Nan::ForceSet(object, v8::Local<v8::String>(key), value, v8::PropertyAttribute(attributes));
}
else {
Nan::Set(object, v8::Local<v8::String>(key), value);
}
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
}
template<>
inline void node::Object::set_property(v8::Isolate* isolate, const v8::Local<v8::Object> &object, uint32_t index, const v8::Local<v8::Value> &value) {
Nan::TryCatch trycatch;
Nan::Set(object, index, value);
if (trycatch.HasCaught()) {
throw node::Exception(isolate, trycatch.Exception());
}
}
template<>
inline std::vector<node::String> node::Object::get_property_names(v8::Isolate* isolate, const v8::Local<v8::Object> &object) {
auto maybe_array = Nan::GetPropertyNames(object);
if (maybe_array.IsEmpty()) {
return std::vector<node::String>();
}
auto array = maybe_array.ToLocalChecked();
uint32_t count = array->Length();
std::vector<node::String> names;
names.reserve(count);
for (uint32_t i = 0; i < count; i++) {
names.push_back(array->Get(i)->ToString());
}
return names;
}
template<>
inline v8::Local<v8::Value> node::Object::get_prototype(v8::Isolate* isolate, const v8::Local<v8::Object> &object) {
return object->GetPrototype();
}
template<>
inline void node::Object::set_prototype(v8::Isolate* isolate, const v8::Local<v8::Object> &object, const v8::Local<v8::Value> &prototype) {
Nan::SetPrototype(object, prototype);
}
template<>
inline v8::Local<v8::Object> node::Object::create_empty(v8::Isolate* isolate) {
return Nan::New<v8::Object>();
}
template<>
inline v8::Local<v8::Object> node::Object::create_array(v8::Isolate* isolate, uint32_t length, const v8::Local<v8::Value> values[]) {
v8::Local<v8::Array> array = Nan::New<v8::Array>(length);
for (uint32_t i = 0; i < length; i++) {
set_property(isolate, array, i, values[i]);
}
return array;
}
template<>
inline v8::Local<v8::Object> node::Object::create_date(v8::Isolate* isolate, double time) {
return Nan::New<v8::Date>(time).ToLocalChecked();
}
template<>
template<typename ClassType>
inline v8::Local<v8::Object> node::Object::create_instance(v8::Isolate* isolate, typename ClassType::Internal* internal) {
return node::ObjectWrap<ClassType>::create_instance(isolate, internal);
}
template<>
template<typename ClassType>
inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local<v8::Object> &object) {
return node::ObjectWrap<ClassType>::has_instance(isolate, object);
}
template<>
template<typename ClassType>
inline typename ClassType::Internal* node::Object::get_internal(const v8::Local<v8::Object> &object) {
return *Nan::ObjectWrap::Unwrap<node::ObjectWrap<ClassType>>(object);
}
template<>
template<typename ClassType>
inline void node::Object::set_internal(const v8::Local<v8::Object> &object, typename ClassType::Internal* ptr) {
auto wrap = Nan::ObjectWrap::Unwrap<node::ObjectWrap<ClassType>>(object);
*wrap = ptr;
}
} // js
} // realm

View File

@ -0,0 +1,75 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_class.hpp"
#include "js_object_accessor.hpp"
namespace realm {
// Specialize a native accessor class for Node.
template<>
class NativeAccessor<node::Types::Value, node::Types::Context> : public js::NativeAccessor<node::Types> {};
namespace js {
template<>
inline std::string NativeAccessor<node::Types>::to_binary(v8::Isolate* isolate, v8::Local<v8::Value> &value) {
if (Value::is_array_buffer(isolate, value)) {
// TODO: This probably needs some abstraction for older V8.
#if REALM_V8_ARRAY_BUFFER_API
v8::Local<v8::ArrayBuffer> array_buffer = value.As<v8::ArrayBuffer>();
v8::ArrayBuffer::Contents contents = array_buffer->GetContents();
return std::string(static_cast<char*>(contents.Data()), contents.ByteLength());
#else
// TODO: Implement this for older V8
#endif
}
else if (Value::is_array_buffer_view(isolate, value)) {
Nan::TypedArrayContents<char> contents(value);
return std::string(*contents, contents.length());
}
else if (::node::Buffer::HasInstance(value)) {
return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value));
}
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
}
template<>
inline v8::Local<v8::Value> NativeAccessor<node::Types>::from_binary(v8::Isolate* isolate, BinaryData data) {
#if REALM_V8_ARRAY_BUFFER_API
size_t byte_count = data.size();
void* bytes = nullptr;
if (byte_count) {
bytes = memcpy(malloc(byte_count), data.data(), byte_count);
}
// An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected.
return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
#else
// TODO: Implement this for older V8
#endif
}
} // js
} // realm

View File

@ -0,0 +1,84 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace node {
template<typename MemberType>
class Protected {
// TODO: Figure out why Nan::CopyablePersistentTraits causes a build failure.
Nan::Persistent<MemberType, v8::CopyablePersistentTraits<MemberType>> m_value;
public:
Protected(v8::Local<MemberType> value) : m_value(value) {}
operator v8::Local<MemberType>() const {
return Nan::New(m_value);
}
bool operator==(const v8::Local<MemberType> &other) const {
return m_value == other;
}
bool operator!=(const v8::Local<MemberType> &other) const {
return m_value != other;
}
bool operator==(const Protected<MemberType> &other) const {
return m_value == other.m_value;
}
bool operator!=(const Protected<MemberType> &other) const {
return m_value != other.m_value;
}
};
} // node
namespace js {
template<>
class Protected<node::Types::GlobalContext> : public node::Protected<v8::Context> {
public:
Protected(v8::Local<v8::Context> ctx) : node::Protected<v8::Context>(ctx) {}
operator v8::Isolate*() const {
return v8::Local<v8::Context>(*this)->GetIsolate();
}
};
template<>
class Protected<node::Types::Value> : public node::Protected<v8::Value> {
public:
Protected(v8::Isolate* isolate, v8::Local<v8::Value> value) : node::Protected<v8::Value>(value) {}
};
template<>
class Protected<node::Types::Object> : public node::Protected<v8::Object> {
public:
Protected(v8::Isolate* isolate, v8::Local<v8::Object> object) : node::Protected<v8::Object>(object) {}
};
template<>
class Protected<node::Types::Function> : public node::Protected<v8::Function> {
public:
Protected(v8::Isolate* isolate, v8::Local<v8::Function> object) : node::Protected<v8::Function>(object) {}
};
} // js
} // realm

View File

@ -0,0 +1,65 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
class ReturnValue<node::Types> {
Nan::ReturnValue<v8::Value> m_value;
public:
ReturnValue(Nan::ReturnValue<v8::Value> value) : m_value(value) {}
void set(const v8::Local<v8::Value> &value) {
m_value.Set(value);
}
void set(const std::string &string) {
if (string.empty()) {
m_value.SetEmptyString();
}
else {
m_value.Set(Nan::New(string).ToLocalChecked());
}
}
void set(bool boolean) {
m_value.Set(boolean);
}
void set(double number) {
m_value.Set(number);
}
void set(int32_t number) {
m_value.Set(number);
}
void set(uint32_t number) {
m_value.Set(number);
}
void set_null() {
m_value.SetNull();
}
void set_undefined() {
m_value.SetUndefined();
}
};
} // js
} // realm

45
src/node/node_string.hpp Normal file
View File

@ -0,0 +1,45 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
class String<node::Types> {
std::string m_str;
public:
String(const char* s) : m_str(s) {}
String(const std::string &s) : m_str(s) {}
String(const v8::Local<v8::String> &s) : m_str(*Nan::Utf8String(s)) {}
String(v8::Local<v8::String> &&s) : String(s) {}
operator std::string() const {
return m_str;
}
operator v8::Local<v8::String>() const {
return Nan::New(m_str).ToLocalChecked();
}
};
} // js
} // realm

68
src/node/node_types.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 <cmath>
#include <functional>
#include <map>
#include <string>
#include <nan.h>
#include "js_types.hpp"
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || (V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
#define REALM_V8_ARRAY_BUFFER_API 1
#endif
namespace realm {
namespace node {
struct Types {
using Context = v8::Isolate*;
using GlobalContext = v8::Local<v8::Context>;
using Value = v8::Local<v8::Value>;
using Object = v8::Local<v8::Object>;
using String = v8::Local<v8::String>;
using Function = v8::Local<v8::Function>;
using ConstructorCallback = Nan::FunctionCallback;
using FunctionCallback = Nan::FunctionCallback;
using PropertyGetterCallback = Nan::GetterCallback;
using PropertySetterCallback = Nan::SetterCallback;
using IndexPropertyGetterCallback = Nan::IndexGetterCallback;
using IndexPropertySetterCallback = Nan::IndexSetterCallback;
using StringPropertyGetterCallback = Nan::PropertyGetterCallback;
using StringPropertySetterCallback = Nan::PropertySetterCallback;
using StringPropertyEnumeratorCallback = Nan::PropertyEnumeratorCallback;
};
template<typename ClassType>
class ObjectWrap;
using String = js::String<Types>;
using Context = js::Context<Types>;
using Value = js::Value<Types>;
using Function = js::Function<Types>;
using Object = js::Object<Types>;
using Exception = js::Exception<Types>;
using ReturnValue = js::ReturnValue<Types>;
} // node
} // realm

169
src/node/node_value.hpp Normal file
View File

@ -0,0 +1,169 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include "node_types.hpp"
namespace realm {
namespace js {
template<>
inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsArray();
}
template<>
inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
#if REALM_V8_ARRAY_BUFFER_API
return value->IsArrayBuffer();
#else
// TODO: Implement this!
#endif
}
template<>
inline bool node::Value::is_array_buffer_view(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
#if REALM_V8_ARRAY_BUFFER_API
return value->IsArrayBufferView();
#else
// TODO: Implement this!
#endif
}
template<>
inline bool node::Value::is_date(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsDate();
}
template<>
inline bool node::Value::is_boolean(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsBoolean();
}
template<>
inline bool node::Value::is_constructor(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsFunction();
}
template<>
inline bool node::Value::is_function(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsFunction();
}
template<>
inline bool node::Value::is_null(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsNull();
}
template<>
inline bool node::Value::is_number(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsNumber();
}
template<>
inline bool node::Value::is_object(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsObject();
}
template<>
inline bool node::Value::is_string(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsString();
}
template<>
inline bool node::Value::is_undefined(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsUndefined();
}
template<>
inline bool node::Value::is_valid(const v8::Local<v8::Value> &value) {
return !value.IsEmpty();
}
template<>
inline v8::Local<v8::Value> node::Value::from_boolean(v8::Isolate* isolate, bool boolean) {
return Nan::New(boolean);
}
template<>
inline v8::Local<v8::Value> node::Value::from_null(v8::Isolate* isolate) {
return Nan::Null();
}
template<>
inline v8::Local<v8::Value> node::Value::from_number(v8::Isolate* isolate, double number) {
return Nan::New(number);
}
template<>
inline v8::Local<v8::Value> node::Value::from_string(v8::Isolate* isolate, const node::String &string) {
return v8::Local<v8::String>(string);
}
template<>
inline v8::Local<v8::Value> node::Value::from_undefined(v8::Isolate* isolate) {
return Nan::Undefined();
}
template<>
inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return Nan::To<bool>(value).FromMaybe(false);
}
template<>
inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
double number = Nan::To<double>(value).FromMaybe(NAN);
if (isnan(number)) {
throw std::invalid_argument("Value not convertible to a number.");
}
return number;
}
template<>
inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->ToString();
}
template<>
inline v8::Local<v8::Object> node::Value::to_object(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return Nan::To<v8::Object>(value).FromMaybe(v8::Local<v8::Object>());
}
template<>
inline v8::Local<v8::Object> node::Value::to_array(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return to_object(isolate, value);
}
template<>
inline v8::Local<v8::Object> node::Value::to_date(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return to_object(isolate, value);
}
template<>
inline v8::Local<v8::Function> node::Value::to_function(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsFunction() ? v8::Local<v8::Function>::Cast(value) : v8::Local<v8::Function>();
}
template<>
inline v8::Local<v8::Function> node::Value::to_constructor(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return to_function(isolate, value);
}
} // js
} // realm

View File

@ -16,26 +16,25 @@
//
////////////////////////////////////////////////////////////////////////////
#include "rpc.hpp"
#include <cassert>
#include <dlfcn.h>
#include <map>
#include <string>
#include "js_init.h"
#include "js_object.hpp"
#include "js_results.hpp"
#include "js_list.hpp"
#include "js_realm.hpp"
#include "js_util.hpp"
#include "rpc.hpp"
#include "jsc_init.hpp"
#include "jsc_types.hpp"
#include "base64.hpp"
#include "object_accessor.hpp"
#include "shared_realm.hpp"
#include "results.hpp"
#include <cassert>
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm_js;
using namespace realm;
using namespace realm::rpc;
using Accessor = NativeAccessor<JSValueRef, JSContextRef>;
static const char * const RealmObjectTypesData = "data";
static const char * const RealmObjectTypesDate = "date";
@ -59,9 +58,8 @@ RPCServer::RPCServer() {
m_requests["/create_session"] = [this](const json dict) {
RJSInitializeInContext(m_context);
JSStringRef realm_string = RJSStringForString("Realm");
JSObjectRef realm_constructor = RJSValidatedObjectProperty(m_context, JSContextGetGlobalObject(m_context), realm_string);
JSStringRelease(realm_string);
jsc::String realm_string = "Realm";
JSObjectRef realm_constructor = jsc::Object::validated_get_constructor(m_context, JSContextGetGlobalObject(m_context), realm_string);
m_session_id = store_object(realm_constructor);
return (json){{"result", m_session_id}};
@ -80,88 +78,72 @@ RPCServer::RPCServer() {
arg_values[i] = deserialize_json_value(args[i]);
}
JSValueRef exception = NULL;
JSObjectRef realm_object = JSObjectCallAsConstructor(m_context, realm_constructor, arg_count, arg_values, &exception);
if (exception) {
return (json){{"error", RJSStringForValue(m_context, exception)}};
}
JSObjectRef realm_object = jsc::Function::construct(m_context, realm_constructor, arg_count, arg_values);
RPCObjectID realm_id = store_object(realm_object);
return (json){{"result", realm_id}};
};
m_requests["/begin_transaction"] = [this](const json dict) {
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->begin_transaction();
SharedRealm realm = *jsc::Object::get_internal<js::RealmClass<jsc::Types>>(m_objects[realm_id]);
realm->begin_transaction();
return json::object();
};
m_requests["/cancel_transaction"] = [this](const json dict) {
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->cancel_transaction();
SharedRealm realm = *jsc::Object::get_internal<js::RealmClass<jsc::Types>>(m_objects[realm_id]);
realm->cancel_transaction();
return json::object();
};
m_requests["/commit_transaction"] = [this](const json dict) {
RPCObjectID realm_id = dict["realmId"].get<RPCObjectID>();
RJSGetInternal<realm::SharedRealm *>(m_objects[realm_id])->get()->commit_transaction();
SharedRealm realm = *jsc::Object::get_internal<js::RealmClass<jsc::Types>>(m_objects[realm_id]);
realm->commit_transaction();
return json::object();
};
m_requests["/call_method"] = [this](const json dict) {
JSObjectRef object = m_objects[dict["id"].get<RPCObjectID>()];
JSStringRef method_string = RJSStringForString(dict["name"].get<std::string>());
JSObjectRef function = RJSValidatedObjectProperty(m_context, object, method_string);
JSStringRelease(method_string);
std::string method_string = dict["name"].get<std::string>();
JSObjectRef function = jsc::Object::validated_get_function(m_context, object, method_string);
json args = dict["arguments"];
size_t count = args.size();
JSValueRef arg_values[count];
for (size_t i = 0; i < count; i++) {
size_t arg_count = args.size();
JSValueRef arg_values[arg_count];
for (size_t i = 0; i < arg_count; i++) {
arg_values[i] = deserialize_json_value(args[i]);
}
JSValueRef exception = NULL;
JSValueRef result = JSObjectCallAsFunction(m_context, function, object, count, arg_values, &exception);
if (exception) {
return (json){{"error", RJSStringForValue(m_context, exception)}};
}
JSValueRef result = jsc::Function::call(m_context, function, object, arg_count, arg_values);
return (json){{"result", serialize_json_value(result)}};
};
m_requests["/get_property"] = [this](const json dict) {
RPCObjectID oid = dict["id"].get<RPCObjectID>();
json name = dict["name"];
JSValueRef value = NULL;
JSValueRef exception = NULL;
JSValueRef value;
if (name.is_number()) {
value = JSObjectGetPropertyAtIndex(m_context, m_objects[oid], name.get<unsigned int>(), &exception);
value = jsc::Object::get_property(m_context, m_objects[oid], name.get<unsigned int>());
}
else {
JSStringRef prop_string = RJSStringForString(name.get<std::string>());
value = JSObjectGetProperty(m_context, m_objects[oid], prop_string, &exception);
JSStringRelease(prop_string);
value = jsc::Object::get_property(m_context, m_objects[oid], name.get<std::string>());
}
if (exception) {
return (json){{"error", RJSStringForValue(m_context, exception)}};
}
return (json){{"result", serialize_json_value(value)}};
};
m_requests["/set_property"] = [this](const json dict) {
RPCObjectID oid = dict["id"].get<RPCObjectID>();
json name = dict["name"];
JSValueRef value = deserialize_json_value(dict["value"]);
JSValueRef exception = NULL;
if (name.is_number()) {
JSObjectSetPropertyAtIndex(m_context, m_objects[oid], name.get<unsigned int>(), value, &exception);
jsc::Object::set_property(m_context, m_objects[oid], name.get<unsigned int>(), value);
}
else {
JSStringRef prop_string = RJSStringForString(name.get<std::string>());
JSObjectSetProperty(m_context, m_objects[oid], prop_string, value, 0, &exception);
JSStringRelease(prop_string);
jsc::Object::set_property(m_context, m_objects[oid], name.get<std::string>(), value);
}
if (exception) {
return (json){{"error", RJSStringForValue(m_context, exception)}};
}
return json::object();
};
m_requests["/dispose_object"] = [this](const json dict) {
@ -181,7 +163,7 @@ RPCServer::RPCServer() {
m_objects.erase(object.first);
}
JSGarbageCollect(m_context);
RJSClearTestState();
js::delete_all_realms();
return json::object();
};
}
@ -206,7 +188,7 @@ json RPCServer::perform_request(std::string name, json &args) {
return {{"error", "Invalid session ID"}};
}
} catch (std::exception &exception) {
return {{"error", (std::string)"exception thrown: " + exception.what()}};
return {{"error", exception.what()}};
}
}
@ -218,34 +200,34 @@ RPCObjectID RPCServer::store_object(JSObjectRef object) {
return next_id;
}
json RPCServer::serialize_json_value(JSValueRef value) {
switch (JSValueGetType(m_context, value)) {
json RPCServer::serialize_json_value(JSValueRef js_value) {
switch (JSValueGetType(m_context, js_value)) {
case kJSTypeUndefined:
return json::object();
case kJSTypeNull:
return {{"value", json(nullptr)}};
case kJSTypeBoolean:
return {{"value", JSValueToBoolean(m_context, value)}};
return {{"value", jsc::Value::to_boolean(m_context, js_value)}};
case kJSTypeNumber:
return {{"value", JSValueToNumber(m_context, value, NULL)}};
return {{"value", jsc::Value::to_number(m_context, js_value)}};
case kJSTypeString:
return {{"value", RJSStringForValue(m_context, value)}};
return {{"value", jsc::Value::to_string(m_context, js_value)}};
case kJSTypeObject:
break;
}
JSObjectRef js_object = JSValueToObject(m_context, value, NULL);
JSObjectRef js_object = jsc::Value::validated_to_object(m_context, js_value);
if (JSValueIsObjectOfClass(m_context, value, RJSObjectClass())) {
realm::Object *object = RJSGetInternal<realm::Object *>(js_object);
if (jsc::Object::is_instance<js::RealmObjectClass<jsc::Types>>(m_context, js_object)) {
auto object = jsc::Object::get_internal<js::RealmObjectClass<jsc::Types>>(js_object);
return {
{"type", RealmObjectTypesObject},
{"id", store_object(js_object)},
{"schema", serialize_object_schema(object->get_object_schema())}
};
}
else if (JSValueIsObjectOfClass(m_context, value, RJSListClass())) {
realm::List *list = RJSGetInternal<realm::List *>(js_object);
else if (jsc::Object::is_instance<js::ListClass<jsc::Types>>(m_context, js_object)) {
auto list = jsc::Object::get_internal<js::ListClass<jsc::Types>>(js_object);
return {
{"type", RealmObjectTypesList},
{"id", store_object(js_object)},
@ -253,8 +235,8 @@ json RPCServer::serialize_json_value(JSValueRef value) {
{"schema", serialize_object_schema(list->get_object_schema())}
};
}
else if (JSValueIsObjectOfClass(m_context, value, RJSResultsClass())) {
realm::Results *results = RJSGetInternal<realm::Results *>(js_object);
else if (jsc::Object::is_instance<js::ResultsClass<jsc::Types>>(m_context, js_object)) {
auto results = jsc::Object::get_internal<js::ResultsClass<jsc::Types>>(js_object);
return {
{"type", RealmObjectTypesResults},
{"id", store_object(js_object)},
@ -262,44 +244,40 @@ json RPCServer::serialize_json_value(JSValueRef value) {
{"schema", serialize_object_schema(results->get_object_schema())}
};
}
else if (RJSIsValueArray(m_context, value)) {
size_t length = RJSValidatedListLength(m_context, js_object);
else if (jsc::Value::is_array(m_context, js_object)) {
uint32_t length = jsc::Object::validated_get_length(m_context, js_object);
std::vector<json> array;
for (unsigned int i = 0; i < length; i++) {
array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(m_context, js_object, i, NULL)));
for (uint32_t i = 0; i < length; i++) {
array.push_back(serialize_json_value(jsc::Object::get_property(m_context, js_object, i)));
}
return {{"value", array}};
}
else if (RJSIsValueArrayBuffer(m_context, value)) {
std::string data = RJSAccessor::to_binary(m_context, value);
else if (jsc::Value::is_array_buffer(m_context, js_object)) {
std::string data = Accessor::to_binary(m_context, js_value);
return {
{"type", RealmObjectTypesData},
{"value", base64_encode((unsigned char *)data.data(), data.size())},
};
}
else if (RJSIsValueDate(m_context, value)) {
else if (jsc::Value::is_date(m_context, js_object)) {
return {
{"type", RealmObjectTypesDate},
{"value", RJSValidatedValueToNumber(m_context, value)},
{"value", jsc::Value::to_number(m_context, js_object)},
};
}
else {
// Serialize this JS object as a plain object since it doesn't match any known types above.
JSPropertyNameArrayRef js_keys = JSObjectCopyPropertyNames(m_context, js_object);
size_t count = JSPropertyNameArrayGetCount(js_keys);
std::vector<jsc::String> js_keys = jsc::Object::get_property_names(m_context, js_object);
std::vector<std::string> keys;
std::vector<json> values;
for (size_t i = 0; i < count; i++) {
JSStringRef js_key = JSPropertyNameArrayGetNameAtIndex(js_keys, i);
JSValueRef js_value = RJSValidatedPropertyValue(m_context, js_object, js_key);
for (auto &js_key : js_keys) {
JSValueRef js_value = jsc::Object::get_property(m_context, js_object, js_key);
keys.push_back(RJSStringForJSString(js_key));
values.push_back(serialize_json_value(js_value));
keys.push_back(js_key);
values.push_back(js_value);
}
JSPropertyNameArrayRelease(js_keys);
return {
{"type", RealmObjectTypesDictionary},
{"keys", keys},
@ -312,7 +290,7 @@ json RPCServer::serialize_json_value(JSValueRef value) {
json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema) {
std::vector<std::string> properties;
for (realm::Property prop : object_schema.properties) {
for (auto &prop : object_schema.properties) {
properties.push_back(prop.name);
}
@ -322,8 +300,7 @@ json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema
};
}
JSValueRef RPCServer::deserialize_json_value(const json dict)
{
JSValueRef RPCServer::deserialize_json_value(const json dict) {
json oid = dict["id"];
if (oid.is_number()) {
return m_objects[oid.get<RPCObjectID>()];
@ -331,28 +308,24 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
json value = dict["value"];
json type = dict["type"];
if (type.is_string()) {
std::string type_string = type.get<std::string>();
if (type_string == RealmObjectTypesFunction) {
// FIXME: Make this actually call the function by its id once we need it to.
JSStringRef js_body = JSStringCreateWithUTF8CString("");
JSObjectRef js_function = JSObjectMakeFunction(m_context, NULL, 0, NULL, js_body, NULL, 1, NULL);
JSStringRelease(js_body);
return js_function;
return JSObjectMakeFunction(m_context, NULL, 0, NULL, jsc::String(""), NULL, 1, NULL);
}
else if (type_string == RealmObjectTypesDictionary) {
JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL);
JSObjectRef js_object = jsc::Object::create_empty(m_context);
json keys = dict["keys"];
json values = dict["values"];
size_t count = keys.size();
for (size_t i = 0; i < count; i++) {
JSStringRef js_key = RJSStringForString(keys.at(i));
std::string js_key = keys.at(i);
JSValueRef js_value = deserialize_json_value(values.at(i));
JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL);
JSStringRelease(js_key);
jsc::Object::set_property(m_context, js_object, js_key, js_value);
}
return js_object;
@ -362,35 +335,28 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
if (!base64_decode(value.get<std::string>(), &bytes)) {
throw std::runtime_error("Failed to decode base64 encoded data");
}
return RJSAccessor::from_binary(m_context, realm::BinaryData(bytes));
return Accessor::from_binary(m_context, realm::BinaryData(bytes));
}
else if (type_string == RealmObjectTypesDate) {
JSValueRef exception = NULL;
JSValueRef time = JSValueMakeNumber(m_context, value.get<double>());
JSObjectRef date = JSObjectMakeDate(m_context, 1, &time, &exception);
if (exception) {
throw RJSException(m_context, exception);
}
return date;
return jsc::Object::create_date(m_context, value.get<double>());
}
else if (type_string == RealmObjectTypesUndefined) {
return JSValueMakeUndefined(m_context);
return jsc::Value::from_undefined(m_context);
}
assert(0);
}
if (value.is_null()) {
return JSValueMakeNull(m_context);
return jsc::Value::from_null(m_context);
}
else if (value.is_boolean()) {
return JSValueMakeBoolean(m_context, value.get<bool>());
return jsc::Value::from_boolean(m_context, value.get<bool>());
}
else if (value.is_number()) {
return JSValueMakeNumber(m_context, value.get<double>());
return jsc::Value::from_number(m_context, value.get<double>());
}
else if (value.is_string()) {
return RJSValueForString(m_context, value.get<std::string>());
return jsc::Value::from_string(m_context, value.get<std::string>());
}
else if (value.is_array()) {
size_t count = value.size();
@ -400,7 +366,7 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
js_values[i] = deserialize_json_value(value.at(i));
}
return JSObjectMakeArray(m_context, count, js_values, NULL);
return jsc::Object::create_array(m_context, (uint32_t)count, js_values);
}
assert(0);
}

View File

@ -19,13 +19,13 @@
#pragma once
#include "json.hpp"
#include <JavaScriptCore/JSBase.h>
#include "jsc_types.hpp"
namespace realm {
class ObjectSchema;
}
namespace realm_js {
class ObjectSchema;
namespace rpc {
using json = nlohmann::json;
using RPCObjectID = u_int64_t;
@ -48,8 +48,8 @@ class RPCServer {
json serialize_json_value(JSValueRef value);
JSValueRef deserialize_json_value(const json dict);
json serialize_object_schema(const realm::ObjectSchema &objectSchema);
json serialize_object_schema(const ObjectSchema &objectSchema);
};
}
} // rpc
} // realm

63
tests/index.js Normal file
View File

@ -0,0 +1,63 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/* eslint-env es6, node */
/* eslint-disable no-console */
'use strict';
const mockery = require('mockery');
function runTests() {
const RealmTests = require('./js');
const testNames = RealmTests.getTestNames();
let passed = true;
for (let suiteName in testNames) {
console.log('Starting ' + suiteName);
for (let testName of testNames[suiteName]) {
RealmTests.runTest(suiteName, 'beforeEach');
try {
RealmTests.runTest(suiteName, testName);
console.log('+ ' + testName);
}
catch (e) {
console.warn('- ' + testName);
console.error(e.message, e.stack);
passed = false;
}
finally {
RealmTests.runTest(suiteName, 'afterEach');
}
}
}
return passed;
}
if (require.main == module) {
mockery.enable();
mockery.warnOnUnregistered(false);
mockery.registerMock('realm', require('..'));
if (!runTests()) {
process.exit(1);
}
}

1
tests/js/query-tests.json Symbolic link
View File

@ -0,0 +1 @@
../../src/object-store/tests/query.json

View File

@ -19,7 +19,6 @@
'use strict';
const React = require('react-native');
const Realm = require('realm');
const RealmTests = require('realm-tests');
RealmTests.registerTests({
@ -56,8 +55,6 @@ function runTests() {
let testNames = RealmTests.getTestNames();
for (let suiteName in testNames) {
let testSuite = RealmTests[suiteName];
console.log('Starting ' + suiteName);
for (let testName of testNames[suiteName]) {