All tests now pass on Node

This commit is contained in:
Scott Kyle 2016-04-14 12:39:17 -07:00
parent bd4088ce91
commit 133289ad85
17 changed files with 348 additions and 215 deletions

View File

@ -5,7 +5,6 @@
"sources": [
"src/js_realm.cpp",
"src/node/node_init.cpp",
"src/node/node_object_accessor.cpp",
"src/object-store/src/index_set.cpp",
"src/object-store/src/list.cpp",
"src/object-store/src/object_schema.cpp",

View File

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

View File

@ -48,6 +48,7 @@
},
"dependencies": {
"bindings": "^1.2.1",
"mockery": "^1.6.2",
"nan": "^2.2.1",
"node-gyp": "^3.3.1",
"rnpm": "1.5.2",

View File

@ -50,11 +50,10 @@
F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; };
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */; };
F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; };
F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */; };
F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; };
F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.c */; };
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; };
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; };
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; };
@ -174,7 +173,6 @@
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier.hpp; path = src/impl/weak_realm_notifier.hpp; sourceTree = "<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>"; };
F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_object_accessor.cpp; sourceTree = "<group>"; };
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = "<group>"; };
F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = "<group>"; };
F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = "<group>"; };
@ -187,7 +185,7 @@
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.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node_dummy.c; 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>"; };
@ -410,13 +408,12 @@
F62BF9001CAC72C40022BCDC /* Node */ = {
isa = PBXGroup;
children = (
F6267BCA1CADC49200AC36B1 /* node_dummy.c */,
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */,
F60102CF1CBB814A00EC01BA /* node_init.hpp */,
F620F0571CB766DA0082977B /* node_init.cpp */,
F620F0551CB655A50082977B /* node_class.hpp */,
F6874A351CAC792D00EEEE36 /* node_types.hpp */,
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */,
F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */,
);
name = Node;
path = node;
@ -707,7 +704,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cp ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"";
shellScript = "cp -f ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"";
};
F63FF2C51C12462600B3B8E0 /* Download Core */ = {
isa = PBXShellScriptBuildPhase;
@ -741,7 +738,6 @@
buildActionMask = 2147483647;
files = (
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */,
F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */,
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */,
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */,
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */,
@ -752,7 +748,7 @@
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */,
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */,
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */,
F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */,
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */,
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */,
F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */,
F620F0581CB766DA0082977B /* node_init.cpp in Sources */,

View File

@ -18,8 +18,8 @@
#pragma once
#include <list>
#include <map>
#include <set>
#include "js_class.hpp"
#include "js_types.hpp"
@ -67,10 +67,20 @@ class RealmDelegate : public BindingContext {
}
void add_notification(TFunction notification) {
m_notifications.insert(Protected<TFunction>(m_context, notification));
for (auto &handler : m_notifications) {
if (handler == notification) {
return;
}
}
m_notifications.emplace_back(m_context, notification);
}
void remove_notification(TFunction notification) {
m_notifications.erase(Protected<TFunction>(m_context, notification));
for (auto iter = m_notifications.begin(); iter != m_notifications.end(); ++iter) {
if (*iter == notification) {
m_notifications.erase(iter);
return;
}
}
}
void remove_all_notifications() {
m_notifications.clear();
@ -81,7 +91,7 @@ class RealmDelegate : public BindingContext {
private:
Protected<TGlobalContext> m_context;
std::set<Protected<TFunction>> m_notifications;
std::list<Protected<TFunction>> m_notifications;
std::weak_ptr<Realm> m_realm;
void notify(const char *notification_name) {
@ -142,11 +152,11 @@ class Realm {
static void GetDefaultPath(TContext, TObject, ReturnValue &);
static void SetDefaultPath(TContext, TObject, TValue value);
static TObject create_constructor(TContext ctx) {
TObject realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
TObject collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
TObject list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
TObject results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
static TFunction create_constructor(TContext ctx) {
TFunction realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
TFunction collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
TFunction list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
TFunction 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);

View File

@ -73,6 +73,7 @@ struct Value {
static bool is_array(TContext, const TValue &);
static bool is_array_buffer(TContext, const TValue &);
static bool is_array_buffer_view(TContext, const TValue &);
static bool is_boolean(TContext, const TValue &);
static bool is_constructor(TContext, const TValue &);
static bool is_date(TContext, const TValue &);
@ -239,7 +240,6 @@ class Protected {
bool operator!=(const TValue &) const;
bool operator==(const Protected<TValue> &) const;
bool operator!=(const Protected<TValue> &) const;
bool operator<(const Protected<TValue> &) const;
};
template<typename T>
@ -249,6 +249,8 @@ struct Exception : public std::runtime_error {
const Protected<TValue> m_value;
Exception(TContext ctx, const std::string &message)
: std::runtime_error(message), m_value(value(ctx, message)) {}
Exception(TContext ctx, const TValue &val)
: std::runtime_error(std::string(Value<T>::to_string(ctx, val))), m_value(ctx, val) {}

View File

@ -40,9 +40,11 @@ template<typename ClassType>
class ObjectWrap {
public:
using Internal = typename ClassType::Internal;
operator Internal*() const {
return m_object.get();
}
ObjectWrap<ClassType>& operator=(Internal* object) {
if (m_object.get() != object) {
m_object = std::unique_ptr<Internal>(object);
@ -134,7 +136,7 @@ private:
return index_setter(ctx, object, index, value, exception);
}
else {
*exception = Exception::value(ctx, std::string("Cannot assigned to read only index ") + util::to_string(index));
*exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index));
return false;
}
}
@ -288,33 +290,9 @@ class ObjectWrap<void> {
public:
using Internal = void;
operator Internal*() const {
return nullptr;
}
ObjectWrap<void>& operator=(Internal* object) {
return *this;
}
static JSClassRef get_class() {
return nullptr;
}
static JSClassRef get_constructor_class() {
return nullptr;
}
static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) {
return nullptr;
}
static JSObjectRef create_constructor(JSContextRef ctx) {
return nullptr;
}
static bool has_instance(JSContextRef ctx, JSValueRef value) {
return nullptr;
}
};
// The declared static variables must be defined as well.

View File

@ -27,39 +27,8 @@ extern "C" {
using namespace realm;
using namespace realm::jsc;
JSValueRef RJSTypeGet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
std::string str = String(propertyName);
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return Value::from_string(ctx, str);
}
JSClassRef RJSRealmTypeClass() {
JSClassDefinition realmTypesDefinition = kJSClassDefinitionEmpty;
realmTypesDefinition.className = "PropTypes";
JSStaticValue types[] = {
{ "BOOL", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "INT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "FLOAT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DOUBLE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "STRING", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DATE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DATA", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "OBJECT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ NULL, NULL, NULL, 0 }
};
realmTypesDefinition.staticValues = types;
return JSClassCreate(&realmTypesDefinition);
}
JSObjectRef RJSConstructorCreate(JSContextRef ctx) {
JSObjectRef realm_constructor = js::Realm<Types>::create_constructor(ctx);
JSObjectRef types_object = JSObjectMake(ctx, RJSRealmTypeClass(), nullptr);
// TODO: Either remove this (preferable) or move implementation to JS.
jsc::Object::set_property(ctx, realm_constructor, "Types", types_object, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete));
return realm_constructor;
return js::Realm<Types>::create_constructor(ctx);
}
void RJSInitializeInContext(JSContextRef ctx) {

View File

@ -57,21 +57,6 @@ class Protected {
operator T() const {
return m_value;
}
bool operator==(const T &other) const {
return m_value == other;
}
bool operator!=(const T &other) const {
return m_value != other;
}
bool operator==(const Protected<T> &other) const {
return m_value == other;
}
bool operator!=(const Protected<T> &other) const {
return m_value != other;
}
bool operator<(const Protected<T> &other) const {
return m_value < other.m_value;
}
};
template<typename T>

View File

@ -20,6 +20,7 @@
#include "node_types.hpp"
#include "js_class.hpp"
#include "js_util.hpp"
namespace realm {
namespace node {
@ -45,9 +46,15 @@ static inline std::vector<v8::Local<v8::Value>> get_arguments(const Nan::Functio
return arguments;
}
static inline void setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
Nan::HandleScope scope;
static inline void 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);
}
static inline void 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();
@ -58,54 +65,83 @@ static inline void setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::
fn->SetName(fn_name);
}
static inline void setup_property(v8::Local<v8::ObjectTemplate> tpl, const std::string &name, const PropertyType &property) {
Nan::HandleScope scope;
static inline 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 inline 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());
}
template<typename TargetType>
static inline void 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 = static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::DontDelete | (property.setter ? v8::None : v8::ReadOnly));
v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete);
Nan::SetAccessor(tpl, prop_name, property.getter, property.setter, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
}
template<typename T>
template<typename ClassType>
class ObjectWrap : public Nan::ObjectWrap {
static ClassDefinition<T> s_class;
static Nan::Persistent<v8::Function> s_constructor;
static Nan::Persistent<v8::FunctionTemplate> s_template;
using Internal = typename ClassType::Internal;
std::unique_ptr<T> m_object;
static ClassType s_class;
ObjectWrap(T* object = nullptr) : m_object(object) {}
std::unique_ptr<Internal> m_object;
public:
operator T*() const {
return m_object.get();
}
ObjectWrap<T>& operator=(T* object) {
if (m_object.get() != object) {
m_object = std::unique_ptr<T>(object);
}
return *this;
ObjectWrap(Internal* object = nullptr) : m_object(object) {}
template<typename U>
static v8::Local<v8::FunctionTemplate> get_superclass(ClassDefinition<U>*) {
return ObjectWrap<U>::get_template();
}
static v8::Local<v8::Object> create_instance(v8::Isolate* isolate, T* internal = nullptr) {
static void get_nonexistent_property(v8::Local<v8::String> property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) {
// Do nothing. This function exists only to prevent a crash where it is used.
}
static void set_property(v8::Local<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);
}
}
static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) {
uint32_t length;
try {
length = Object::validated_get_length(info.GetIsolate(), info.This());
}
catch (std::exception &) {
// Enumerating properties should never throw an exception.
return;
}
v8::Local<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);
}
static v8::Local<v8::FunctionTemplate> create_template() {
Nan::EscapableHandleScope scope;
// TODO: Figure out why this template ends up being empty here.
v8::Local<v8::FunctionTemplate> tpl = Nan::New(s_template);
v8::Local<v8::Object> instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked();
auto wrap = new ObjectWrap<T>(internal);
wrap->Wrap(instance);
return scope.Escape(instance);
}
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return Nan::New(s_template)->HasInstance(value);
}
static NAN_MODULE_INIT(init) {
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();
@ -113,32 +149,81 @@ class ObjectWrap : public Nan::ObjectWrap {
tpl->SetClassName(name);
instance_tpl->SetInternalFieldCount(1);
// TODO: Setup static properties and methods.
v8::Local<v8::FunctionTemplate> super_tpl = get_superclass(s_class.superclass);
if (!super_tpl.IsEmpty()) {
tpl->Inherit(super_tpl);
}
// Static properties are setup in create_constructor() because V8.
for (auto &pair : s_class.static_methods) {
setup_static_method(tpl, pair.first, pair.second);
}
for (auto &pair : s_class.methods) {
setup_method(tpl, pair.first, pair.second);
}
for (auto &pair : s_class.properties) {
setup_property(instance_tpl, pair.first, pair.second);
setup_property<v8::ObjectTemplate>(instance_tpl, pair.first, pair.second);
}
if (s_class.index_accessor.getter) {
// TODO: Add our own index enumerator.
auto &index_accessor = s_class.index_accessor;
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter);
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
}
if (s_class.string_accessor.getter) {
if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) {
// Use our own wrapper for the setter since we want to throw for negative indices.
auto &string_accessor = s_class.string_accessor;
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter, string_accessor.setter, 0, 0, string_accessor.enumerator);
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
}
return scope.Escape(tpl);
}
public:
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;
}
static v8::Local<v8::FunctionTemplate> get_template() {
static Nan::Persistent<v8::FunctionTemplate> js_template(create_template());
return Nan::New(js_template);
}
static v8::Local<v8::Function> create_constructor(v8::Isolate* isolate) {
Nan::EscapableHandleScope scope;
v8::Local<v8::FunctionTemplate> tpl = get_template();
v8::Local<v8::Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
s_constructor.Reset(constructor);
s_template.Reset(tpl);
Nan::Set(target, name, constructor);
for (auto &pair : s_class.static_properties) {
setup_property<v8::Object>(constructor, pair.first, pair.second);
}
static NAN_METHOD(construct) {
return scope.Escape(constructor);
}
static v8::Local<v8::Object> create_instance(v8::Isolate* isolate, Internal* internal = nullptr) {
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);
}
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return get_template()->HasInstance(value);
}
static void construct(Nan::NAN_METHOD_ARGS_TYPE info) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Constructor must be called with new");
}
@ -148,7 +233,7 @@ class ObjectWrap : public Nan::ObjectWrap {
v8::Local<v8::Object> this_object = info.This();
info.GetReturnValue().Set(this_object);
auto wrap = new ObjectWrap<T>();
auto wrap = new ObjectWrap<ClassType>();
wrap->Wrap(this_object);
try {
@ -164,10 +249,19 @@ class ObjectWrap : public Nan::ObjectWrap {
}
};
template<>
class ObjectWrap<void> {
public:
using Internal = void;
static v8::Local<v8::FunctionTemplate> get_template() {
return v8::Local<v8::FunctionTemplate>();;
}
};
// The declared static variables must be defined as well.
template<typename T> ClassDefinition<T> ObjectWrap<T>::s_class;
template<typename T> Nan::Persistent<v8::Function> ObjectWrap<T>::s_constructor;
template<typename T> Nan::Persistent<v8::FunctionTemplate> ObjectWrap<T>::s_template;
template<typename ClassType>
ClassType ObjectWrap<ClassType>::s_class;
} // node
@ -190,7 +284,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) {
}
}
Type::template<node::PropertyType::GetterType F>
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());
@ -220,6 +314,10 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) {
try {
F(isolate, info.This(), index, return_value);
}
catch (std::out_of_range &) {
// Out-of-bounds index getters should just return undefined in JS.
return_value.set_undefined();
}
catch(std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
}
@ -229,7 +327,10 @@ template<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 {
F(isolate, info.This(), index, value);
if (F(isolate, info.This(), index, value)) {
// Indicate that the property was intercepted.
info.GetReturnValue().Set(value);
}
}
catch(std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));
@ -252,7 +353,10 @@ template<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 {
F(isolate, info.This(), property, value);
if (F(isolate, info.This(), property, value)) {
// Indicate that the property was intercepted.
info.GetReturnValue().Set(value);
}
}
catch(std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));

View File

@ -16,4 +16,15 @@
//
////////////////////////////////////////////////////////////////////////////
void node_module_register(void* mod) {}
// NOTE: This dummy file exists only to make Xcode build the Realm Node dynamic library.
#include "node.h"
extern "C" void node_module_register(void* mod) {}
namespace node {
namespace Buffer {
bool HasInstance(v8::Local<v8::Value> val) { return false; }
char* Data(v8::Local<v8::Value> val) { return nullptr; }
size_t Length(v8::Local<v8::Value> val) { return 0; }
}
}

View File

@ -22,13 +22,10 @@ namespace realm {
namespace node {
static void init(v8::Local<v8::Object> exports) {
ObjectWrap<SharedRealm>::init(exports);
ObjectWrap<realm::List>::init(exports);
// RealmObjectWrap::Init(exports);
// RealmResultsWrap::Init(exports);
// RealmListWrap::Init(exports);
//
// NODE_SET_METHOD(exports, "__clearCaches", ClearCaches);
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Function> realm_constructor = js::Realm<Types>::create_constructor(isolate);
Nan::Set(exports, realm_constructor->GetName(), realm_constructor);
}
} // node

View File

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

View File

@ -27,4 +27,49 @@ namespace realm {
template<>
class NativeAccessor<node::Types::Value, node::Types::Context> : public js::NativeAccessor<node::Types> {};
namespace js {
template<>
inline std::string NativeAccessor<node::Types>::to_binary(v8::Isolate* isolate, v8::Local<v8::Value> &value) {
if (Value::is_array_buffer(isolate, value)) {
// TODO: This probably needs some abstraction for older V8.
#if REALM_V8_ARRAY_BUFFER_API
v8::Local<v8::ArrayBuffer> array_buffer = value.As<v8::ArrayBuffer>();
v8::ArrayBuffer::Contents contents = array_buffer->GetContents();
return std::string(static_cast<char*>(contents.Data()), contents.ByteLength());
#else
// TODO: Implement this for older V8
#endif
}
else if (Value::is_array_buffer_view(isolate, value)) {
Nan::TypedArrayContents<char> contents(value);
return std::string(*contents, contents.length());
}
else if (::node::Buffer::HasInstance(value)) {
return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value));
}
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
}
template<>
inline v8::Local<v8::Value> NativeAccessor<node::Types>::from_binary(v8::Isolate* isolate, BinaryData data) {
#if REALM_V8_ARRAY_BUFFER_API
size_t byte_count = data.size();
void* bytes = nullptr;
if (byte_count) {
bytes = memcpy(malloc(byte_count), data.data(), byte_count);
}
// An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected.
return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
#else
// TODO: Implement this for older V8
#endif
}
} // js
} // realm

View File

@ -27,6 +27,10 @@
#include "js_types.hpp"
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || (V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
#define REALM_V8_ARRAY_BUFFER_API 1
#endif
namespace realm {
namespace node {
@ -72,9 +76,6 @@ class Protected {
bool operator!=(const Protected<T> &other) const {
return m_value != other.m_value;
}
bool operator<(const Protected<T> &other) const {
return *Nan::New(m_value) < *Nan::New(other.m_value);
}
};
template<typename T>
@ -188,7 +189,20 @@ inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local<v8::Valu
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<>
@ -440,27 +454,27 @@ inline v8::Local<v8::Object> node::Object::create_date(v8::Isolate* isolate, dou
}
template<>
template<typename U>
inline v8::Local<v8::Object> node::Object::create_instance(v8::Isolate* isolate, U* internal) {
return node::ObjectWrap<U>::create_instance(isolate, internal);
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 U>
template<typename ClassType>
inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local<v8::Object> &object) {
return node::ObjectWrap<U>::has_instance(isolate, object);
return node::ObjectWrap<ClassType>::has_instance(isolate, object);
}
template<>
template<typename U>
inline U* node::Object::get_internal(const v8::Local<v8::Object> &object) {
return *Nan::ObjectWrap::Unwrap<node::ObjectWrap<U>>(object);
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 U>
inline void node::Object::set_internal(const v8::Local<v8::Object> &object, U* ptr) {
auto wrap = Nan::ObjectWrap::Unwrap<node::ObjectWrap<U>>(object);
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;
}

38
tests/index.js Normal file
View File

@ -0,0 +1,38 @@
'use strict';
const mockery = require('mockery');
function runTests() {
const RealmTests = require('./js');
const testNames = RealmTests.getTestNames();
for (let suiteName in testNames) {
let testSuite = RealmTests[suiteName];
console.log('Starting ' + suiteName);
for (let testName of testNames[suiteName]) {
RealmTests.runTest(suiteName, 'beforeEach');
try {
RealmTests.runTest(suiteName, testName);
console.log('+ ' + testName);
}
catch (e) {
console.warn('- ' + testName);
console.error(e.message, e.stack);
}
finally {
RealmTests.runTest(suiteName, 'afterEach');
}
}
}
}
if (require.main == module) {
mockery.enable();
mockery.warnOnUnregistered(false);
mockery.registerMock('realm', require('..'));
runTests();
}

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

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