Merge pull request #1023 from realm/tg/object-accessor-updates
Update to work with new object store object accessor API
This commit is contained in:
commit
b78bcefc67
|
@ -1,5 +1,5 @@
|
|||
PACKAGE_NAME=realm-js
|
||||
VERSION=1.3.1
|
||||
REALM_CORE_VERSION=2.6.0
|
||||
REALM_SYNC_VERSION=1.8.3
|
||||
REALM_OBJECT_SERVER_VERSION=1.3.0
|
||||
REALM_CORE_VERSION=2.8.1
|
||||
REALM_SYNC_VERSION=1.9.2
|
||||
REALM_OBJECT_SERVER_VERSION=1.7.1
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "js_collection.hpp"
|
||||
#include "js_object_accessor.hpp"
|
||||
#include "js_realm_object.hpp"
|
||||
#include "js_results.hpp"
|
||||
#include "js_types.hpp"
|
||||
|
@ -32,12 +33,15 @@
|
|||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename JSEngine>
|
||||
class NativeAccessor;
|
||||
|
||||
template<typename T>
|
||||
class List : public realm::List {
|
||||
public:
|
||||
List(std::shared_ptr<realm::Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {}
|
||||
List(const realm::List &l) : realm::List(l) {}
|
||||
|
||||
|
||||
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;
|
||||
};
|
||||
|
||||
|
@ -104,7 +108,7 @@ typename T::Object ListClass<T>::create_instance(ContextType ctx, realm::List li
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
void ListClass<T>::get_length(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
return_value.set((uint32_t)list->size());
|
||||
}
|
||||
|
@ -120,7 +124,8 @@ void ListClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t index,
|
|||
template<typename T>
|
||||
bool ListClass<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);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), &list->get_object_schema());
|
||||
list->set(accessor, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -129,8 +134,9 @@ void ListClass<T>::push(ContextType ctx, FunctionType, ObjectType this_object, s
|
|||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), &list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->add(ctx, arguments[i]);
|
||||
list->add(accessor, arguments[i]);
|
||||
}
|
||||
|
||||
return_value.set((uint32_t)list->size());
|
||||
|
@ -160,8 +166,9 @@ void ListClass<T>::unshift(ContextType ctx, FunctionType, ObjectType this_object
|
|||
validate_argument_count_at_least(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), &list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->insert(ctx, arguments[i], i);
|
||||
list->insert(accessor, i, arguments[i]);
|
||||
}
|
||||
|
||||
return_value.set((uint32_t)list->size());
|
||||
|
@ -207,6 +214,7 @@ void ListClass<T>::splice(ContextType ctx, FunctionType, ObjectType this_object,
|
|||
std::vector<ValueType> removed_objects;
|
||||
removed_objects.reserve(remove);
|
||||
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), &list->get_object_schema());
|
||||
for (size_t i = 0; i < remove; i++) {
|
||||
auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index));
|
||||
|
||||
|
@ -214,7 +222,7 @@ void ListClass<T>::splice(ContextType ctx, FunctionType, ObjectType this_object,
|
|||
list->remove(index);
|
||||
}
|
||||
for (size_t i = 2; i < argc; i++) {
|
||||
list->insert(ctx, arguments[i], index + i - 2);
|
||||
list->insert(accessor, index + i - 2, arguments[i]);
|
||||
}
|
||||
|
||||
return_value.set(Object::create_array(ctx, removed_objects));
|
||||
|
|
|
@ -23,136 +23,235 @@
|
|||
#include "js_schema.hpp"
|
||||
|
||||
namespace realm {
|
||||
class List;
|
||||
class Object;
|
||||
class ObjectSchema;
|
||||
class Realm;
|
||||
class Results;
|
||||
struct Property;
|
||||
|
||||
namespace js {
|
||||
namespace _impl {
|
||||
template<typename JSEngine, typename T>
|
||||
struct Unbox;
|
||||
}
|
||||
|
||||
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>;
|
||||
template<typename JSEngine>
|
||||
class NativeAccessor {
|
||||
public:
|
||||
using ContextType = typename JSEngine::Context;
|
||||
using ObjectType = typename JSEngine::Object;
|
||||
using ValueType = typename JSEngine::Value;
|
||||
using Object = js::Object<JSEngine>;
|
||||
using Value = js::Value<JSEngine>;
|
||||
using OptionalValue = util::Optional<ValueType>;
|
||||
|
||||
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);
|
||||
}
|
||||
NativeAccessor(ContextType ctx, std::shared_ptr<Realm> realm=nullptr, const ObjectSchema* object_schema=nullptr)
|
||||
: m_ctx(ctx), m_realm(std::move(realm)), m_object_schema(object_schema) { }
|
||||
|
||||
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);
|
||||
}
|
||||
NativeAccessor(NativeAccessor& parent, const Property& prop)
|
||||
: m_ctx(parent.m_ctx)
|
||||
, m_realm(parent.m_realm)
|
||||
, m_object_schema(&*m_realm->schema().find(prop.object_type))
|
||||
{ }
|
||||
|
||||
// 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 Timestamp to_timestamp(ContextType ctx, ValueType &value) {
|
||||
ObjectType date = Value::validated_to_date(ctx, value, "Property");
|
||||
double milliseconds = Value::to_number(ctx, date);
|
||||
int64_t seconds = milliseconds / 1000;
|
||||
int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000;
|
||||
return Timestamp(seconds, nanoseconds);
|
||||
}
|
||||
static ValueType from_timestamp(ContextType ctx, Timestamp ts) {
|
||||
return Object::create_date(ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000);
|
||||
}
|
||||
|
||||
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)) {
|
||||
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
|
||||
if (realm_object->realm() == realm) {
|
||||
return realm_object->row().get_index();
|
||||
}
|
||||
OptionalValue value_for_property(ValueType dict, std::string const& prop_name, size_t) {
|
||||
ObjectType object = Value::validated_to_object(m_ctx, dict);
|
||||
if (!Object::has_property(m_ctx, object, prop_name)) {
|
||||
return util::none;
|
||||
}
|
||||
return Object::get_property(m_ctx, object, prop_name);
|
||||
}
|
||||
|
||||
auto object_schema = realm->schema().find(type);
|
||||
if (Value::is_array(ctx, object)) {
|
||||
object = Schema<T>::dict_for_property_array(ctx, *object_schema, object);
|
||||
OptionalValue default_value_for_property(const ObjectSchema &object_schema, const std::string &prop_name) {
|
||||
auto defaults = get_delegate<JSEngine>(m_realm.get())->m_defaults[object_schema.name];
|
||||
auto it = defaults.find(prop_name);
|
||||
return it != defaults.end() ? util::make_optional(ValueType(it->second)) : util::none;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T unbox(ValueType value, bool create = false, bool update = false);
|
||||
|
||||
ValueType box(bool boolean) { return Value::from_boolean(m_ctx, boolean); }
|
||||
ValueType box(int64_t number) { return Value::from_number(m_ctx, number); }
|
||||
ValueType box(float number) { return Value::from_number(m_ctx, number); }
|
||||
ValueType box(double number) { return Value::from_number(m_ctx, number); }
|
||||
ValueType box(StringData string) { return Value::from_string(m_ctx, string.data()); }
|
||||
ValueType box(Mixed) { throw std::runtime_error("'Any' type is unsupported"); }
|
||||
|
||||
ValueType box(BinaryData);
|
||||
ValueType box(Timestamp ts) {
|
||||
return Object::create_date(m_ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000);
|
||||
}
|
||||
ValueType box(realm::Object realm_object) {
|
||||
return RealmObjectClass<JSEngine>::create_instance(m_ctx, std::move(realm_object));
|
||||
}
|
||||
ValueType box(realm::List list) {
|
||||
return ListClass<JSEngine>::create_instance(m_ctx, std::move(list));
|
||||
}
|
||||
ValueType box(realm::Results results) {
|
||||
return ResultsClass<JSEngine>::create_instance(m_ctx, std::move(results));
|
||||
}
|
||||
|
||||
bool is_null(ValueType const& value) {
|
||||
return Value::is_null(m_ctx, value) || Value::is_undefined(m_ctx, value);
|
||||
}
|
||||
ValueType null_value() {
|
||||
return Value::from_null(m_ctx);
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
void enumerate_list(ValueType& value, Fn&& func) {
|
||||
auto obj = Value::validated_to_object(m_ctx, value);
|
||||
uint32_t size = Object::validated_get_length(m_ctx, obj);
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
func(Object::validated_get_object(m_ctx, obj, i));
|
||||
}
|
||||
|
||||
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, SharedRealm realm, ValueType &value) {
|
||||
ObjectType object = Value::validated_to_object(ctx, value);
|
||||
if (!Object::template is_instance<RealmObjectClass<T>>(ctx, object)) {
|
||||
throw std::runtime_error("object is not a Realm Object");
|
||||
}
|
||||
|
||||
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
|
||||
if (realm_object->realm() != realm) {
|
||||
throw std::runtime_error("Realm object is from another Realm");
|
||||
|
||||
}
|
||||
return realm_object->row().get_index();
|
||||
}
|
||||
static ValueType from_object(ContextType ctx, realm::Object realm_object) {
|
||||
return RealmObjectClass<T>::create_instance(ctx, std::move(realm_object));
|
||||
}
|
||||
|
||||
static size_t list_size(ContextType ctx, ValueType &value) {
|
||||
return Object::validated_get_length(ctx, Value::validated_to_object(ctx, value));
|
||||
bool allow_missing(ValueType const&) const noexcept { return false; }
|
||||
void will_change(realm::Object&, realm::Property const&) { }
|
||||
void did_change() { }
|
||||
|
||||
std::string print(ValueType const&) { return "not implemented"; }
|
||||
|
||||
private:
|
||||
ContextType m_ctx;
|
||||
std::shared_ptr<Realm> m_realm;
|
||||
const ObjectSchema* m_object_schema = nullptr;
|
||||
std::string m_string_buffer;
|
||||
|
||||
template<typename, typename>
|
||||
friend struct _impl::Unbox;
|
||||
};
|
||||
|
||||
namespace _impl {
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, bool> {
|
||||
static bool call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_boolean(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, int64_t> {
|
||||
static int64_t call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
static ValueType from_list(ContextType ctx, realm::List list) {
|
||||
return ListClass<T>::create_instance(ctx, std::move(list));
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, float> {
|
||||
static float call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
static ValueType from_results(ContextType ctx, realm::Results results) {
|
||||
return ResultsClass<T>::create_instance(ctx, std::move(results));
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, double> {
|
||||
static double call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
static Mixed to_mixed(ContextType ctx, ValueType &val) {
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<bool>> {
|
||||
static util::Optional<bool> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_boolean(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<int64_t>> {
|
||||
static util::Optional<int64_t> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<float>> {
|
||||
static util::Optional<float> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, util::Optional<double>> {
|
||||
static util::Optional<double> call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
return js::Value<JSEngine>::validated_to_number(ctx->m_ctx, value, "Property");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, StringData> {
|
||||
static StringData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
ctx->m_string_buffer = js::Value<JSEngine>::validated_to_string(ctx->m_ctx, value, "Property");
|
||||
return ctx->m_string_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
// Need separate implementations per-engine
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, BinaryData> {
|
||||
static BinaryData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value value, bool, bool);
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, Mixed> {
|
||||
static Mixed call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
throw std::runtime_error("'Any' type is unsupported");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, Timestamp> {
|
||||
static Timestamp call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool, bool) {
|
||||
auto date = js::Value<JSEngine>::validated_to_date(ctx->m_ctx, value, "Property");
|
||||
double milliseconds = js::Value<JSEngine>::to_number(ctx->m_ctx, date);
|
||||
int64_t seconds = milliseconds / 1000;
|
||||
int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000;
|
||||
return Timestamp(seconds, nanoseconds);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, RowExpr> {
|
||||
static RowExpr call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value const& value, bool create, bool try_update) {
|
||||
using Value = js::Value<JSEngine>;
|
||||
using ValueType = typename JSEngine::Value;
|
||||
|
||||
auto object = Value::validated_to_object(ctx->m_ctx, value);
|
||||
if (js::Object<JSEngine>::template is_instance<RealmObjectClass<JSEngine>>(ctx->m_ctx, object)) {
|
||||
auto realm_object = get_internal<JSEngine, RealmObjectClass<JSEngine>>(object);
|
||||
if (realm_object->realm() == ctx->m_realm) {
|
||||
return realm_object->row();
|
||||
}
|
||||
if (!create) {
|
||||
throw std::runtime_error("Realm object is from another Realm");
|
||||
}
|
||||
}
|
||||
else if (!create) {
|
||||
throw std::runtime_error("object is not a Realm Object");
|
||||
}
|
||||
|
||||
if (Value::is_array(ctx->m_ctx, object)) {
|
||||
object = Schema<JSEngine>::dict_for_property_array(ctx->m_ctx, *ctx->m_object_schema, object);
|
||||
}
|
||||
|
||||
auto child = realm::Object::create<ValueType>(*ctx, ctx->m_realm,
|
||||
*ctx->m_object_schema,
|
||||
static_cast<ValueType>(object), try_update);
|
||||
return child.row();
|
||||
}
|
||||
};
|
||||
} // namespace _impl
|
||||
|
||||
template<typename T>
|
||||
template<typename U>
|
||||
U NativeAccessor<T>::unbox(ValueType value, bool create, bool update) {
|
||||
return _impl::Unbox<T, U>::call(this, std::move(value), create, update);
|
||||
}
|
||||
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -79,10 +79,6 @@ class RealmDelegate : public BindingContext {
|
|||
virtual void did_change(std::vector<ObserverState> const& observers, std::vector<void*> const& invalidated, bool version_changed) {
|
||||
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> realm, GlobalContextType ctx) : m_context(ctx), m_realm(realm) {}
|
||||
|
||||
|
@ -159,7 +155,7 @@ class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
|||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
|
||||
using NativeAccessor = realm::js::NativeAccessor<T>;
|
||||
|
||||
public:
|
||||
using ObjectDefaultsMap = typename Schema<T>::ObjectDefaultsMap;
|
||||
|
@ -353,8 +349,9 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
|||
static const String encryption_key_string = "encryptionKey";
|
||||
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());
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(encryption_key_value);
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
|
@ -469,9 +466,9 @@ void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType thi
|
|||
realm::Realm::Config config;
|
||||
config.path = normalize_realm_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());
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(arguments[1]);
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
auto version = realm::Realm::get_schema_version(config);
|
||||
|
@ -556,8 +553,9 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
|||
static const String encryption_key_string = "encryptionKey";
|
||||
ValueType encryption_key_value = Object::get_property(ctx, config_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());
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(encryption_key_value);
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
Protected<ObjectType> thiz(ctx, this_object);
|
||||
|
@ -634,7 +632,8 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, Object
|
|||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
std::string object_type;
|
||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type);
|
||||
auto realm_object = realm::Object::get_for_primary_key(ctx, realm, object_schema, arguments[1]);
|
||||
NativeAccessor accessor(ctx, realm);
|
||||
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, arguments[1]);
|
||||
|
||||
if (realm_object.is_valid()) {
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
|
@ -662,7 +661,8 @@ void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object
|
|||
update = Value::validated_to_boolean(ctx, arguments[2], "update");
|
||||
}
|
||||
|
||||
auto realm_object = realm::Object::create<ValueType>(ctx, realm, object_schema, object, update);
|
||||
NativeAccessor accessor(ctx, realm);
|
||||
auto realm_object = realm::Object::create<ValueType>(accessor, realm, object_schema, object, update);
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||
}
|
||||
|
||||
|
@ -799,4 +799,4 @@ void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object,
|
|||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
} // realm
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename> class NativeAccessor;
|
||||
|
||||
template<typename T>
|
||||
struct RealmObjectClass : ClassDefinition<T, realm::Object> {
|
||||
using ContextType = typename T::Context;
|
||||
|
@ -94,7 +96,9 @@ template<typename T>
|
|||
void RealmObjectClass<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);
|
||||
NativeAccessor<T> accessor(ctx);
|
||||
std::string name = property;
|
||||
auto result = realm_object->template get_property_value<ValueType>(accessor, name);
|
||||
return_value.set(result);
|
||||
} catch (InvalidPropertyException &ex) {
|
||||
// getters for nonexistent properties in JS should always return undefined
|
||||
|
@ -111,7 +115,8 @@ bool RealmObjectClass<T>::set_property(ContextType ctx, ObjectType object, const
|
|||
}
|
||||
|
||||
try {
|
||||
realm_object->set_property_value(ctx, property_name, value, true);
|
||||
NativeAccessor<T> accessor(ctx, realm_object->realm());
|
||||
realm_object->set_property_value(accessor, property_name, value, true);
|
||||
}
|
||||
catch (TypeErrorException &ex) {
|
||||
throw TypeErrorException(realm_object->get_object_schema().name + "." + property_name, ex.type());
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename>
|
||||
class NativeAccessor;
|
||||
|
||||
template<typename T>
|
||||
class Results : public realm::Results {
|
||||
public:
|
||||
|
@ -117,10 +120,9 @@ typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &co
|
|||
auto const &realm = collection.get_realm();
|
||||
auto const &object_schema = collection.get_object_schema();
|
||||
|
||||
std::vector<ValueType> args(&arguments[1], &arguments[argc]);
|
||||
|
||||
parser::Predicate predicate = parser::parse(query_string);
|
||||
query_builder::ArgumentConverter<ValueType, ContextType> converter(ctx, realm, args);
|
||||
NativeAccessor<T> accessor(ctx, realm);
|
||||
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &arguments[1], argc - 1);
|
||||
query_builder::apply_predicate(query, predicate, converter, realm->schema(), object_schema.name);
|
||||
|
||||
return create_instance(ctx, realm::Results(realm, std::move(query)));
|
||||
|
|
|
@ -51,7 +51,6 @@ class UserClass : public ClassDefinition<T, SharedUser> {
|
|||
using Value = js::Value<T>;
|
||||
using Function = js::Function<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
|
||||
|
||||
public:
|
||||
std::string const name = "User";
|
||||
|
@ -337,7 +336,6 @@ class SyncClass : public ClassDefinition<T, void *> {
|
|||
using Value = js::Value<T>;
|
||||
using Function = js::Function<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
|
||||
|
||||
public:
|
||||
std::string const name = "Sync";
|
||||
|
|
|
@ -24,13 +24,12 @@
|
|||
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) {
|
||||
template<>
|
||||
inline BinaryData NativeAccessor<jsc::Types>::unbox(ValueType value, bool, bool) {
|
||||
static jsc::String s_array_buffer = "ArrayBuffer";
|
||||
static jsc::String s_buffer = "buffer";
|
||||
static jsc::String s_byte_length = "byteLength";
|
||||
|
@ -38,25 +37,25 @@ inline std::string NativeAccessor<jsc::Types>::to_binary(JSContextRef ctx, JSVal
|
|||
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);
|
||||
JSObjectRef global_object = JSContextGetGlobalObject(m_ctx);
|
||||
JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(m_ctx, global_object, s_array_buffer);
|
||||
JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(m_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)) {
|
||||
if (JSValueIsInstanceOfConstructor(m_ctx, value, array_buffer_constructor, nullptr)) {
|
||||
uint8_array_arguments[0] = value;
|
||||
uint8_array_argc = 1;
|
||||
}
|
||||
else if (JSObjectRef object = JSValueToObject(ctx, value, nullptr)) {
|
||||
else if (JSObjectRef object = JSValueToObject(m_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);
|
||||
JSValueRef is_view = jsc::Object::call_method(m_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);
|
||||
if (jsc::Value::to_boolean(m_ctx, is_view)) {
|
||||
uint8_array_arguments[0] = jsc::Object::validated_get_object(m_ctx, object, s_buffer);
|
||||
uint8_array_arguments[1] = jsc::Object::get_property(m_ctx, object, s_byte_offset);
|
||||
uint8_array_arguments[2] = jsc::Object::get_property(m_ctx, object, s_byte_length);
|
||||
uint8_array_argc = 3;
|
||||
}
|
||||
}
|
||||
|
@ -65,34 +64,34 @@ inline std::string NativeAccessor<jsc::Types>::to_binary(JSContextRef ctx, JSVal
|
|||
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);
|
||||
JSObjectRef uint8_array = jsc::Function::construct(m_ctx, uint8_array_constructor, uint8_array_argc, uint8_array_arguments);
|
||||
uint32_t byte_count = jsc::Object::validated_get_length(m_ctx, uint8_array);
|
||||
m_string_buffer.resize(byte_count);
|
||||
|
||||
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);
|
||||
JSValueRef byteValue = jsc::Object::get_property(m_ctx, uint8_array, i);
|
||||
m_string_buffer[i] = jsc::Value::to_number(m_ctx, byteValue);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
return BinaryData(m_string_buffer.data(), m_string_buffer.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline JSValueRef NativeAccessor<jsc::Types>::from_binary(JSContextRef ctx, BinaryData data) {
|
||||
inline JSValueRef NativeAccessor<jsc::Types>::box(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);
|
||||
JSValueRef byte_count_value = jsc::Value::from_number(m_ctx, byte_count);
|
||||
JSObjectRef uint8_array_constructor = jsc::Object::validated_get_constructor(m_ctx, JSContextGetGlobalObject(m_ctx), s_uint8_array);
|
||||
JSObjectRef uint8_array = jsc::Function::construct(m_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);
|
||||
JSValueRef num = jsc::Value::from_number(m_ctx, data[i]);
|
||||
jsc::Object::set_property(m_ctx, uint8_array, i, num);
|
||||
}
|
||||
|
||||
return jsc::Object::validated_get_object(ctx, uint8_array, s_buffer);
|
||||
return jsc::Object::validated_get_object(m_ctx, uint8_array, s_buffer);
|
||||
}
|
||||
|
||||
} // js
|
||||
|
|
|
@ -24,38 +24,39 @@
|
|||
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)) {
|
||||
template<>
|
||||
inline BinaryData NativeAccessor<node::Types>::unbox(ValueType value, bool, bool) {
|
||||
if (Value::is_array_buffer(m_ctx, 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());
|
||||
m_string_buffer = 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)) {
|
||||
else if (Value::is_array_buffer_view(m_ctx, value)) {
|
||||
Nan::TypedArrayContents<char> contents(value);
|
||||
|
||||
return std::string(*contents, contents.length());
|
||||
m_string_buffer = std::string(*contents, contents.length());
|
||||
}
|
||||
else if (::node::Buffer::HasInstance(value)) {
|
||||
return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value));
|
||||
m_string_buffer = std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
|
||||
}
|
||||
|
||||
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
|
||||
return BinaryData(m_string_buffer.data(), m_string_buffer.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> NativeAccessor<node::Types>::from_binary(v8::Isolate* isolate, BinaryData data) {
|
||||
inline v8::Local<v8::Value> NativeAccessor<node::Types>::box(BinaryData data) {
|
||||
#if REALM_V8_ARRAY_BUFFER_API
|
||||
size_t byte_count = data.size();
|
||||
void* bytes = nullptr;
|
||||
|
@ -65,7 +66,7 @@ inline v8::Local<v8::Value> NativeAccessor<node::Types>::from_binary(v8::Isolate
|
|||
}
|
||||
|
||||
// An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected.
|
||||
return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
|
||||
return v8::ArrayBuffer::New(m_ctx, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
|
||||
#else
|
||||
// TODO: Implement this for older V8
|
||||
#endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 872a154b1bfd843f7dad98cab73e23a5aff2ee66
|
||||
Subproject commit 6decdfbb88494908c8116b64b21c8715175fc2f3
|
|
@ -32,7 +32,7 @@
|
|||
using namespace realm;
|
||||
using namespace realm::rpc;
|
||||
|
||||
using Accessor = NativeAccessor<JSValueRef, JSContextRef>;
|
||||
using Accessor = realm::js::NativeAccessor<jsc::Types>;
|
||||
|
||||
static const char * const RealmObjectTypesData = "data";
|
||||
static const char * const RealmObjectTypesDate = "date";
|
||||
|
@ -413,7 +413,8 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
|
|||
return {{"value", array}};
|
||||
}
|
||||
else if (jsc::Value::is_array_buffer(m_context, js_object)) {
|
||||
std::string data = Accessor::to_binary(m_context, js_value);
|
||||
Accessor accessor(m_context);
|
||||
auto data = accessor.unbox<realm::BinaryData>(js_value);
|
||||
return {
|
||||
{"type", RealmObjectTypesData},
|
||||
{"value", base64_encode((unsigned char *)data.data(), data.size())},
|
||||
|
@ -511,7 +512,7 @@ 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 Accessor::from_binary(m_context, realm::BinaryData(bytes));
|
||||
return Accessor(m_context).box(realm::BinaryData(bytes.data(), bytes.size()));
|
||||
}
|
||||
else if (type_string == RealmObjectTypesDate) {
|
||||
return jsc::Object::create_date(m_context, value.get<double>());
|
||||
|
|
Loading…
Reference in New Issue