Include property names in type error exceptions thrown by Realm.create (#1069)
* Add and adopt `Value::is_binary` / `Value::to_binary` / `Value::from_binary`. These methods allow conversions between `BinaryData` and the equivalent JavaScript types without using `NativeAccessor`. Instead, `NativeAccessor` now itself delegates to these methods. * Have `NativeAccessor::value_for_property` and `RealmObjectClass::set_property` verify that values are valid for the property in question. If not, we throw an exception that includes the name and type of the property in question. `NativeAccessor` is changed to always hold a reference to a `Realm` and an `ObjectSchema` in order to make this validation possible. * Fix the Windows build. * Remove an unused, incorrect forward declaration of a template class named `Realm` that caused ambiguity with object store's `Realm` class. * Disambiguate between `realm::js::PropertyType` and `realm::PropertyType`. * Update CHANGELOG.md
This commit is contained in:
parent
775bd8e2ff
commit
30e58022cf
|
@ -4,6 +4,7 @@ vNext Release notes (TBD)
|
|||
* None
|
||||
|
||||
### Enhancements
|
||||
* Better error messages when creating objects.
|
||||
* Added bundled TypeScript declarations of the Realm API.
|
||||
|
||||
### Bug fixes
|
||||
|
|
|
@ -26,6 +26,7 @@ LOCAL_SRC_FILES := vendor/base64.cpp
|
|||
LOCAL_SRC_FILES += src/js_realm.cpp
|
||||
LOCAL_SRC_FILES += src/rpc.cpp
|
||||
LOCAL_SRC_FILES += src/jsc/jsc_init.cpp
|
||||
LOCAL_SRC_FILES += src/jsc/jsc_value.cpp
|
||||
LOCAL_SRC_FILES += src/android/io_realm_react_RealmReactModule.cpp
|
||||
LOCAL_SRC_FILES += src/android/jsc_override.cpp
|
||||
LOCAL_SRC_FILES += src/android/platform.cpp
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
504CF8601EBCAE3600A9A4B6 /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF85A1EBCAE3600A9A4B6 /* sync_file.cpp */; };
|
||||
504CF8611EBCAE3600A9A4B6 /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF85C1EBCAE3600A9A4B6 /* sync_metadata.cpp */; };
|
||||
50C671001E1D2D31003CB63C /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D02C7791E1C83650048C13E /* thread_safe_reference.cpp */; };
|
||||
5D1BF0571EF1DB4800B7DC87 /* jsc_value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D1BF0561EF1DB4800B7DC87 /* jsc_value.cpp */; };
|
||||
5D25F5A11D6284FD00EBBB30 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF3301C16434400B3B8E0 /* libz.tbd */; };
|
||||
8507156E1E2CFCD000E548DB /* object_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8507156B1E2CFC0100E548DB /* object_notifier.cpp */; };
|
||||
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
|
||||
|
@ -191,13 +192,12 @@
|
|||
5D02C7781E1C83650048C13E /* execution_context_id.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = execution_context_id.hpp; path = src/execution_context_id.hpp; sourceTree = "<group>"; };
|
||||
5D02C7791E1C83650048C13E /* thread_safe_reference.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = thread_safe_reference.cpp; path = src/thread_safe_reference.cpp; sourceTree = "<group>"; };
|
||||
5D02C77A1E1C83650048C13E /* thread_safe_reference.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = thread_safe_reference.hpp; path = src/thread_safe_reference.hpp; sourceTree = "<group>"; };
|
||||
5D1BF0561EF1DB4800B7DC87 /* jsc_value.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_value.cpp; sourceTree = "<group>"; };
|
||||
8507156B1E2CFC0100E548DB /* object_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = object_notifier.cpp; sourceTree = "<group>"; };
|
||||
8507156C1E2CFC0100E548DB /* object_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = object_notifier.hpp; sourceTree = "<group>"; };
|
||||
850715AE1E32366B00E548DB /* event_loop_dispatcher.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = event_loop_dispatcher.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>"; };
|
||||
|
@ -548,7 +548,6 @@
|
|||
F60103121CC4CBF000EC01BA /* node_exception.hpp */,
|
||||
F60103071CC4B3DF00EC01BA /* node_protected.hpp */,
|
||||
F60103151CC4CCFD00EC01BA /* node_return_value.hpp */,
|
||||
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */,
|
||||
F67478481CC81F1300F9273C /* platform.cpp */,
|
||||
);
|
||||
name = Node;
|
||||
|
@ -695,12 +694,12 @@
|
|||
F60103131CC4CC4500EC01BA /* jsc_string.hpp */,
|
||||
F60103091CC4B5E800EC01BA /* jsc_context.hpp */,
|
||||
F601030B1CC4B6C900EC01BA /* jsc_value.hpp */,
|
||||
5D1BF0561EF1DB4800B7DC87 /* jsc_value.cpp */,
|
||||
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;
|
||||
|
@ -905,6 +904,7 @@
|
|||
F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */,
|
||||
02E315D21DB80DF200555337 /* sync_file.cpp in Sources */,
|
||||
022BF1021E7266DF00F382F1 /* binding_callback_thread_observer.cpp in Sources */,
|
||||
5D1BF0571EF1DB4800B7DC87 /* jsc_value.cpp in Sources */,
|
||||
02F59EC21C88F17D007F774C /* object_store.cpp in Sources */,
|
||||
02022A7C1DA47EC8000F0C4F /* format.cpp in Sources */,
|
||||
02E315CB1DB80DDD00555337 /* sync_user.cpp in Sources */,
|
||||
|
|
|
@ -124,7 +124,7 @@ 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);
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), &list->get_object_schema());
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
list->set(accessor, index, value);
|
||||
return true;
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ 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());
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->add(accessor, arguments[i]);
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ 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());
|
||||
NativeAccessor<T> accessor(ctx, list->get_realm(), list->get_object_schema());
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
list->insert(accessor, i, arguments[i]);
|
||||
}
|
||||
|
@ -214,7 +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());
|
||||
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));
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "js_realm_object.hpp"
|
||||
#include "js_schema.hpp"
|
||||
|
||||
#include "util/format.hpp"
|
||||
|
||||
namespace realm {
|
||||
class List;
|
||||
class Object;
|
||||
|
@ -46,21 +48,27 @@ public:
|
|||
using Value = js::Value<JSEngine>;
|
||||
using OptionalValue = util::Optional<ValueType>;
|
||||
|
||||
NativeAccessor(ContextType ctx, std::shared_ptr<Realm> realm=nullptr, const ObjectSchema* object_schema=nullptr)
|
||||
NativeAccessor(ContextType ctx, std::shared_ptr<Realm> realm, const ObjectSchema& object_schema)
|
||||
: m_ctx(ctx), m_realm(std::move(realm)), m_object_schema(object_schema) { }
|
||||
|
||||
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))
|
||||
, m_object_schema(*m_realm->schema().find(prop.object_type))
|
||||
{ }
|
||||
|
||||
OptionalValue value_for_property(ValueType dict, std::string const& prop_name, size_t) {
|
||||
OptionalValue value_for_property(ValueType dict, std::string const& prop_name, size_t prop_index) {
|
||||
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);
|
||||
ValueType value = Object::get_property(m_ctx, object, prop_name);
|
||||
const auto& prop = m_object_schema.persisted_properties[prop_index];
|
||||
if (!Value::is_valid_for_property(m_ctx, value, prop)) {
|
||||
throw TypeErrorException(util::format("%1.%2", m_object_schema.name, prop.name),
|
||||
js_type_name_for_property_type(prop.type));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
OptionalValue default_value_for_property(const ObjectSchema &object_schema, const std::string &prop_name) {
|
||||
|
@ -77,9 +85,9 @@ public:
|
|||
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(BinaryData data) { return Value::from_binary(m_ctx, 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);
|
||||
}
|
||||
|
@ -118,8 +126,9 @@ public:
|
|||
private:
|
||||
ContextType m_ctx;
|
||||
std::shared_ptr<Realm> m_realm;
|
||||
const ObjectSchema* m_object_schema = nullptr;
|
||||
const ObjectSchema& m_object_schema;
|
||||
std::string m_string_buffer;
|
||||
OwnedBinaryData m_owned_binary_data;
|
||||
|
||||
template<typename, typename>
|
||||
friend struct _impl::Unbox;
|
||||
|
@ -190,10 +199,12 @@ struct Unbox<JSEngine, StringData> {
|
|||
}
|
||||
};
|
||||
|
||||
// Need separate implementations per-engine
|
||||
template<typename JSEngine>
|
||||
struct Unbox<JSEngine, BinaryData> {
|
||||
static BinaryData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value value, bool, bool);
|
||||
static BinaryData call(NativeAccessor<JSEngine> *ctx, typename JSEngine::Value value, bool, bool) {
|
||||
ctx->m_owned_binary_data = js::Value<JSEngine>::validated_to_binary(ctx->m_ctx, value, "Property");
|
||||
return ctx->m_owned_binary_data.get();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename JSEngine>
|
||||
|
@ -235,11 +246,10 @@ struct Unbox<JSEngine, RowExpr> {
|
|||
}
|
||||
|
||||
if (Value::is_array(ctx->m_ctx, object)) {
|
||||
object = Schema<JSEngine>::dict_for_property_array(ctx->m_ctx, *ctx->m_object_schema, 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,
|
||||
auto child = realm::Object::create<ValueType>(*ctx, ctx->m_realm, ctx->m_object_schema,
|
||||
static_cast<ValueType>(object), try_update);
|
||||
return child.row();
|
||||
}
|
||||
|
|
|
@ -58,9 +58,6 @@ static std::string normalize_realm_path(std::string path) {
|
|||
return path;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class Realm;
|
||||
|
||||
template<typename T>
|
||||
class RealmClass;
|
||||
|
||||
|
@ -349,8 +346,7 @@ 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)) {
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(encryption_key_value);
|
||||
auto encryption_key = Value::validated_to_binary(ctx, encryption_key_value, "encryptionKey");
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
|
@ -466,8 +462,7 @@ 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) {
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(arguments[1]);
|
||||
auto encryption_key = Value::validated_to_binary(ctx, arguments[1], "encryptionKey");
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
|
@ -554,8 +549,7 @@ 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)) {
|
||||
NativeAccessor accessor(ctx);
|
||||
auto encryption_key = accessor.template unbox<BinaryData>(encryption_key_value);
|
||||
auto encryption_key = Value::validated_to_binary(ctx, encryption_key_value, "encryptionKey");
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
|
@ -633,7 +627,7 @@ 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);
|
||||
NativeAccessor accessor(ctx, realm);
|
||||
NativeAccessor accessor(ctx, realm, object_schema);
|
||||
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, arguments[1]);
|
||||
|
||||
if (realm_object.is_valid()) {
|
||||
|
@ -662,7 +656,7 @@ void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object
|
|||
update = Value::validated_to_boolean(ctx, arguments[2], "update");
|
||||
}
|
||||
|
||||
NativeAccessor accessor(ctx, realm);
|
||||
NativeAccessor accessor(ctx, realm, object_schema);
|
||||
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)));
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "object_accessor.hpp"
|
||||
#include "object_store.hpp"
|
||||
#include "util/format.hpp"
|
||||
|
||||
#include "js_class.hpp"
|
||||
#include "js_types.hpp"
|
||||
|
@ -105,7 +106,7 @@ 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);
|
||||
NativeAccessor<T> accessor(ctx);
|
||||
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
|
||||
std::string name = property;
|
||||
auto result = realm_object->template get_property_value<ValueType>(accessor, name);
|
||||
return_value.set(result);
|
||||
|
@ -119,17 +120,18 @@ bool RealmObjectClass<T>::set_property(ContextType ctx, ObjectType object, const
|
|||
auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
|
||||
|
||||
std::string property_name = property;
|
||||
if (!realm_object->get_object_schema().property_for_name(property_name)) {
|
||||
const Property* prop = realm_object->get_object_schema().property_for_name(property_name);
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
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());
|
||||
if (!Value::is_valid_for_property(ctx, value, *prop)) {
|
||||
throw TypeErrorException(util::format("%1.%2", realm_object->get_object_schema().name, property_name),
|
||||
js_type_name_for_property_type(prop->type));
|
||||
}
|
||||
|
||||
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
|
||||
realm_object->set_property_value(accessor, property_name, value, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &co
|
|||
auto const &object_schema = collection.get_object_schema();
|
||||
|
||||
parser::Predicate predicate = parser::parse(query_string);
|
||||
NativeAccessor<T> accessor(ctx, realm);
|
||||
NativeAccessor<T> accessor(ctx, realm, object_schema);
|
||||
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &arguments[1], argc - 1);
|
||||
query_builder::apply_predicate(query, predicate, converter, realm->schema(), object_schema.name);
|
||||
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "execution_context_id.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <realm/binary_data.hpp>
|
||||
#include <realm/util/to_string.hpp>
|
||||
|
||||
#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG)
|
||||
|
@ -106,12 +108,16 @@ struct Value {
|
|||
static bool is_object(ContextType, const ValueType &);
|
||||
static bool is_string(ContextType, const ValueType &);
|
||||
static bool is_undefined(ContextType, const ValueType &);
|
||||
static bool is_binary(ContextType, const ValueType &);
|
||||
static bool is_valid(const ValueType &);
|
||||
|
||||
static bool is_valid_for_property(ContextType, const ValueType&, const Property&);
|
||||
|
||||
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_binary(ContextType, BinaryData);
|
||||
static ValueType from_undefined(ContextType);
|
||||
|
||||
static ObjectType to_array(ContextType, const ValueType &);
|
||||
|
@ -122,6 +128,7 @@ struct Value {
|
|||
static double to_number(ContextType, const ValueType &);
|
||||
static ObjectType to_object(ContextType, const ValueType &);
|
||||
static String<T> to_string(ContextType, const ValueType &);
|
||||
static OwnedBinaryData to_binary(ContextType, ValueType);
|
||||
|
||||
#define VALIDATED(return_t, type) \
|
||||
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
||||
|
@ -140,6 +147,7 @@ struct Value {
|
|||
VALIDATED(double, number)
|
||||
VALIDATED(ObjectType, object)
|
||||
VALIDATED(String<T>, string)
|
||||
VALIDATED(OwnedBinaryData, binary)
|
||||
|
||||
#undef VALIDATED
|
||||
};
|
||||
|
@ -336,5 +344,71 @@ REALM_JS_INLINE void set_internal(const typename T::Object &object, typename Cla
|
|||
Object<T>::template set_internal<ClassType>(object, ptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool Value<T>::is_valid_for_property(ContextType context, const ValueType &value, const Property& prop)
|
||||
{
|
||||
if (prop.is_nullable && (is_null(context, value) || is_undefined(context, value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
using PropertyType = realm::PropertyType;
|
||||
switch (prop.type) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
return is_number(context, value);
|
||||
case PropertyType::Bool:
|
||||
return is_boolean(context, value);
|
||||
case PropertyType::String:
|
||||
return is_string(context, value);
|
||||
case PropertyType::Data:
|
||||
return is_binary(context, value);
|
||||
case PropertyType::Date:
|
||||
return is_date(context, value);
|
||||
case PropertyType::Object:
|
||||
return true;
|
||||
case PropertyType::Array:
|
||||
// FIXME: Do we need to validate the types of the contained objects?
|
||||
return is_array(context, value);
|
||||
|
||||
case PropertyType::Any:
|
||||
case PropertyType::LinkingObjects:
|
||||
return false;
|
||||
}
|
||||
|
||||
REALM_UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string js_type_name_for_property_type(PropertyType type)
|
||||
{
|
||||
switch (type) {
|
||||
case PropertyType::Int:
|
||||
case PropertyType::Float:
|
||||
case PropertyType::Double:
|
||||
return "number";
|
||||
case PropertyType::Bool:
|
||||
return "boolean";
|
||||
case PropertyType::String:
|
||||
return "string";
|
||||
case PropertyType::Date:
|
||||
return "date";
|
||||
case PropertyType::Data:
|
||||
return "binary";
|
||||
case PropertyType::Object:
|
||||
return "object";
|
||||
case PropertyType::Array:
|
||||
return "array";
|
||||
|
||||
case PropertyType::Any:
|
||||
throw std::runtime_error("'Any' type is not supported");
|
||||
case PropertyType::LinkingObjects:
|
||||
throw std::runtime_error("LinkingObjects' type is not supported");
|
||||
}
|
||||
|
||||
REALM_UNREACHABLE();
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#include "jsc_function.hpp"
|
||||
#include "jsc_exception.hpp"
|
||||
#include "jsc_return_value.hpp"
|
||||
#include "jsc_object_accessor.hpp"
|
||||
#include "jsc_class.hpp"
|
||||
|
||||
// FIXME: js_object_accessor.hpp includes js_list.hpp which includes js_object_accessor.hpp.
|
||||
#include "js_object_accessor.hpp"
|
||||
|
||||
#include "js_realm.hpp"
|
||||
|
|
|
@ -1,98 +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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jsc_class.hpp"
|
||||
#include "js_object_accessor.hpp"
|
||||
|
||||
namespace realm {
|
||||
|
||||
// Specialize a native accessor class for JSC.
|
||||
|
||||
namespace js {
|
||||
|
||||
template<>
|
||||
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";
|
||||
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(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(m_ctx, value, array_buffer_constructor, nullptr)) {
|
||||
uint8_array_arguments[0] = value;
|
||||
uint8_array_argc = 1;
|
||||
}
|
||||
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(m_ctx, array_buffer_constructor, s_is_view, 1, &object);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uint8_array_argc) {
|
||||
throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary");
|
||||
}
|
||||
|
||||
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(m_ctx, uint8_array, i);
|
||||
m_string_buffer[i] = jsc::Value::to_number(m_ctx, byteValue);
|
||||
}
|
||||
|
||||
return BinaryData(m_string_buffer.data(), m_string_buffer.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
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(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(m_ctx, data[i]);
|
||||
jsc::Object::set_property(m_ctx, uint8_array, i, num);
|
||||
}
|
||||
|
||||
return jsc::Object::validated_get_object(m_ctx, uint8_array, s_buffer);
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "jsc_types.hpp"
|
||||
#include "jsc_string.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2017 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "jsc_value.hpp"
|
||||
|
||||
#include "jsc_function.hpp"
|
||||
#include "jsc_object.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<>
|
||||
bool jsc::Value::is_binary(JSContextRef ctx, const JSValueRef &value)
|
||||
{
|
||||
static jsc::String s_array_buffer = "ArrayBuffer";
|
||||
static jsc::String s_is_view = "isView";
|
||||
|
||||
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
|
||||
JSObjectRef array_buffer_constructor = jsc::Object::validated_get_constructor(ctx, global_object, s_array_buffer);
|
||||
|
||||
// Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView).
|
||||
if (JSValueIsInstanceOfConstructor(ctx, value, array_buffer_constructor, nullptr)) {
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
|
||||
return jsc::Value::to_boolean(ctx, is_view);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
JSValueRef jsc::Value::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);
|
||||
}
|
||||
|
||||
template<>
|
||||
OwnedBinaryData jsc::Value::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);
|
||||
auto buffer = std::make_unique<char[]>(byte_count);
|
||||
|
||||
for (uint32_t i = 0; i < byte_count; i++) {
|
||||
JSValueRef byteValue = jsc::Object::get_property(ctx, uint8_array, i);
|
||||
buffer[i] = jsc::Value::to_number(ctx, byteValue);
|
||||
}
|
||||
|
||||
return OwnedBinaryData(std::move(buffer), byte_count);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
} // namespace realm
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "jsc_protected.hpp"
|
||||
#include "jsc_types.hpp"
|
||||
#include "jsc_string.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
@ -93,6 +95,9 @@ inline bool jsc::Value::is_string(JSContextRef ctx, const JSValueRef &value) {
|
|||
return JSValueIsString(ctx, value);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool jsc::Value::is_binary(JSContextRef ctx, const JSValueRef &value);
|
||||
|
||||
template<>
|
||||
inline bool jsc::Value::is_undefined(JSContextRef ctx, const JSValueRef &value) {
|
||||
return JSValueIsUndefined(ctx, value);
|
||||
|
@ -128,6 +133,9 @@ inline JSValueRef jsc::Value::from_undefined(JSContextRef ctx) {
|
|||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
template<>
|
||||
JSValueRef jsc::Value::from_binary(JSContextRef ctx, BinaryData data);
|
||||
|
||||
template<>
|
||||
inline bool jsc::Value::to_boolean(JSContextRef ctx, const JSValueRef &value) {
|
||||
return JSValueToBoolean(ctx, value);
|
||||
|
@ -190,5 +198,8 @@ inline JSObjectRef jsc::Value::to_function(JSContextRef ctx, const JSValueRef &v
|
|||
return to_object(ctx, value);
|
||||
}
|
||||
|
||||
template<>
|
||||
OwnedBinaryData jsc::Value::to_binary(JSContextRef ctx, JSValueRef value);
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -26,4 +26,7 @@
|
|||
#include "node_function.hpp"
|
||||
#include "node_exception.hpp"
|
||||
#include "node_return_value.hpp"
|
||||
#include "node_object_accessor.hpp"
|
||||
#include "node_class.hpp"
|
||||
|
||||
// FIXME: js_object_accessor.hpp includes js_list.hpp which includes js_object_accessor.hpp.
|
||||
#include "js_object_accessor.hpp"
|
||||
|
|
|
@ -1,76 +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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "node_class.hpp"
|
||||
#include "js_object_accessor.hpp"
|
||||
|
||||
namespace realm {
|
||||
|
||||
// Specialize a native accessor class for Node.
|
||||
namespace js {
|
||||
|
||||
template<>
|
||||
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();
|
||||
|
||||
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(m_ctx, value)) {
|
||||
Nan::TypedArrayContents<char> contents(value);
|
||||
|
||||
m_string_buffer = std::string(*contents, contents.length());
|
||||
}
|
||||
else if (::node::Buffer::HasInstance(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");
|
||||
}
|
||||
|
||||
return BinaryData(m_string_buffer.data(), m_string_buffer.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
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;
|
||||
|
||||
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(m_ctx, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
|
||||
#else
|
||||
// TODO: Implement this for older V8
|
||||
#endif
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
|
@ -91,6 +91,12 @@ inline bool node::Value::is_undefined(v8::Isolate* isolate, const v8::Local<v8::
|
|||
return value->IsUndefined();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool node::Value::is_binary(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return Value::is_array_buffer(isolate, value) || Value::is_array_buffer_view(isolate, value)
|
||||
|| ::node::Buffer::HasInstance(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool node::Value::is_valid(const v8::Local<v8::Value> &value) {
|
||||
return !value.IsEmpty();
|
||||
|
@ -116,6 +122,23 @@ inline v8::Local<v8::Value> node::Value::from_string(v8::Isolate* isolate, const
|
|||
return v8::Local<v8::String>(string);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> node::Value::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
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> node::Value::from_undefined(v8::Isolate* isolate) {
|
||||
return Nan::Undefined();
|
||||
|
@ -140,6 +163,39 @@ inline node::String node::Value::to_string(v8::Isolate* isolate, const v8::Local
|
|||
return value->ToString();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline OwnedBinaryData node::Value::to_binary(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
// Make a non-null OwnedBinaryData, even when `data` is nullptr.
|
||||
auto make_owned_binary_data = [](const char* data, size_t length) {
|
||||
REALM_ASSERT(data || length == 0);
|
||||
char placeholder;
|
||||
return OwnedBinaryData(data ? data : &placeholder, length);
|
||||
};
|
||||
|
||||
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 make_owned_binary_data(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 make_owned_binary_data(*contents, contents.length());
|
||||
}
|
||||
else if (::node::Buffer::HasInstance(value)) {
|
||||
return make_owned_binary_data(::node::Buffer::Data(value), ::node::Buffer::Length(value));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
|
||||
}
|
||||
}
|
||||
|
||||
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>());
|
||||
|
|
|
@ -412,9 +412,8 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
|
|||
}
|
||||
return {{"value", array}};
|
||||
}
|
||||
else if (jsc::Value::is_array_buffer(m_context, js_object)) {
|
||||
Accessor accessor(m_context);
|
||||
auto data = accessor.unbox<realm::BinaryData>(js_value);
|
||||
else if (jsc::Value::is_binary(m_context, js_object)) {
|
||||
auto data = jsc::Value::to_binary(m_context, js_object);
|
||||
return {
|
||||
{"type", RealmObjectTypesData},
|
||||
{"value", base64_encode((unsigned char *)data.data(), data.size())},
|
||||
|
@ -512,7 +511,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(m_context).box(realm::BinaryData(bytes.data(), bytes.size()));
|
||||
return jsc::Value::from_binary(m_context, realm::BinaryData(bytes.data(), bytes.size()));
|
||||
}
|
||||
else if (type_string == RealmObjectTypesDate) {
|
||||
return jsc::Object::create_date(m_context, value.get<double>());
|
||||
|
|
|
@ -869,5 +869,15 @@ module.exports = {
|
|||
p1.age = "Ten";
|
||||
});
|
||||
}, new Error("PersonObject.age must be of type: number"));
|
||||
},
|
||||
|
||||
testErrorMessageFromInvalidCreate: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||
|
||||
TestCase.assertThrowsException(function() {
|
||||
realm.write(function () {
|
||||
var p1 = realm.create('PersonObject', { name: 'Ari', age: 'Ten' });
|
||||
});
|
||||
}, new Error("PersonObject.age must be of type: number"));
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue