mirror of
https://github.com/status-im/realm-js.git
synced 2025-02-02 09:46:28 +00:00
Merge pull request #393 from realm/sk-node
Support Alternative JS Engines
This commit is contained in:
commit
7ceac728eb
@ -169,9 +169,6 @@ Object.defineProperties(Realm, {
|
||||
Results: {
|
||||
value: Results,
|
||||
},
|
||||
Types: {
|
||||
value: Object.freeze(propTypes),
|
||||
},
|
||||
defaultPath: {
|
||||
get: util.getterForProperty('defaultPath'),
|
||||
set: util.setterForProperty('defaultPath'),
|
||||
|
23
lib/index.js
23
lib/index.js
@ -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;
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 */
|
||||
|
@ -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
93
src/js_class.hpp
Normal 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
|
@ -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
|
||||
|
117
src/js_init.cpp
117
src/js_init.cpp
@ -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());
|
||||
}
|
||||
|
||||
}
|
277
src/js_list.cpp
277
src/js_list.cpp
@ -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;
|
||||
}
|
212
src/js_list.hpp
212
src/js_list.hpp
@ -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
|
||||
|
@ -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
144
src/js_object_accessor.hpp
Normal 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
|
555
src/js_realm.cpp
555
src/js_realm.cpp
@ -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
|
||||
|
502
src/js_realm.hpp
502
src/js_realm.hpp
@ -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
122
src/js_realm_object.hpp
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
301
src/js_types.hpp
Normal 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
|
@ -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);
|
||||
}
|
240
src/js_util.hpp
240
src/js_util.hpp
@ -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
426
src/jsc/jsc_class.hpp
Normal 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
|
||||
|
@ -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
|
@ -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
47
src/jsc/jsc_function.hpp
Normal 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
43
src/jsc/jsc_init.cpp
Normal 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
32
src/jsc/jsc_init.hpp
Normal 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
147
src/jsc/jsc_object.hpp
Normal 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
|
99
src/jsc/jsc_object_accessor.hpp
Normal file
99
src/jsc/jsc_object_accessor.hpp
Normal 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
86
src/jsc/jsc_protected.hpp
Normal 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
|
64
src/jsc/jsc_return_value.hpp
Normal file
64
src/jsc/jsc_return_value.hpp
Normal 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
59
src/jsc/jsc_string.hpp
Normal 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
62
src/jsc/jsc_types.hpp
Normal 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
194
src/jsc/jsc_value.hpp
Normal 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
54
src/node/binding.gyp
Normal 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
404
src/node/node_class.hpp
Normal 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
32
src/node/node_context.hpp
Normal 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
30
src/node/node_dummy.cpp
Normal 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; }
|
||||
}
|
||||
}
|
32
src/node/node_exception.hpp
Normal file
32
src/node/node_exception.hpp
Normal 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
|
49
src/node/node_function.hpp
Normal file
49
src/node/node_function.hpp
Normal 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
34
src/node/node_init.cpp
Normal 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
31
src/node/node_init.hpp
Normal 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
159
src/node/node_object.hpp
Normal 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
|
75
src/node/node_object_accessor.hpp
Normal file
75
src/node/node_object_accessor.hpp
Normal 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
|
84
src/node/node_protected.hpp
Normal file
84
src/node/node_protected.hpp
Normal 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
|
65
src/node/node_return_value.hpp
Normal file
65
src/node/node_return_value.hpp
Normal 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
45
src/node/node_string.hpp
Normal 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
68
src/node/node_types.hpp
Normal 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
169
src/node/node_value.hpp
Normal 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
|
188
src/rpc.cpp
188
src/rpc.cpp
@ -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);
|
||||
}
|
||||
|
14
src/rpc.hpp
14
src/rpc.hpp
@ -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
63
tests/index.js
Normal 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
1
tests/js/query-tests.json
Symbolic link
@ -0,0 +1 @@
|
||||
../../src/object-store/tests/query.json
|
3
tests/react-test-app/tests/index.js
vendored
3
tests/react-test-app/tests/index.js
vendored
@ -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]) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user