//////////////////////////////////////////////////////////////////////////// // // Copyright 2016 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////// #pragma once #include "js_list.hpp" #include "js_realm_object.hpp" #include "js_schema.hpp" #include "util/format.hpp" namespace realm { class List; class Object; class ObjectSchema; class Realm; class Results; struct Property; namespace js { namespace _impl { template struct Unbox; } template class NativeAccessor { public: using ContextType = typename JSEngine::Context; using ObjectType = typename JSEngine::Object; using ValueType = typename JSEngine::Value; using Object = js::Object; using Value = js::Value; using OptionalValue = util::Optional; NativeAccessor(ContextType ctx, std::shared_ptr 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)) { } 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; } 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) { auto defaults = get_delegate(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 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(BinaryData data) { return Value::from_binary(m_ctx, data); } ValueType box(Mixed) { throw std::runtime_error("'Any' type is unsupported"); } 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::create_instance(m_ctx, std::move(realm_object)); } ValueType box(realm::List list) { return ListClass::create_instance(m_ctx, std::move(list)); } ValueType box(realm::Results results) { return ResultsClass::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 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)); } } 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 m_realm; const ObjectSchema& m_object_schema; std::string m_string_buffer; OwnedBinaryData m_owned_binary_data; template friend struct _impl::Unbox; }; namespace _impl { template struct Unbox { static bool call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_boolean(ctx->m_ctx, value, "Property"); } }; template struct Unbox { static int64_t call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox { static float call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox { static double call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox> { static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_boolean(ctx->m_ctx, value, "Property"); } }; template struct Unbox> { static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox> { static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox> { static util::Optional call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { return js::Value::validated_to_number(ctx->m_ctx, value, "Property"); } }; template struct Unbox { static StringData call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { ctx->m_string_buffer = js::Value::validated_to_string(ctx->m_ctx, value, "Property"); return ctx->m_string_buffer; } }; template struct Unbox { static BinaryData call(NativeAccessor *ctx, typename JSEngine::Value value, bool, bool) { ctx->m_owned_binary_data = js::Value::validated_to_binary(ctx->m_ctx, value, "Property"); return ctx->m_owned_binary_data.get(); } }; template struct Unbox { static Mixed call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { throw std::runtime_error("'Any' type is unsupported"); } }; template struct Unbox { static Timestamp call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool, bool) { auto date = js::Value::validated_to_date(ctx->m_ctx, value, "Property"); double milliseconds = js::Value::to_number(ctx->m_ctx, date); int64_t seconds = milliseconds / 1000; int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000; return Timestamp(seconds, nanoseconds); } }; template struct Unbox { static RowExpr call(NativeAccessor *ctx, typename JSEngine::Value const& value, bool create, bool try_update) { using Value = js::Value; using ValueType = typename JSEngine::Value; auto object = Value::validated_to_object(ctx->m_ctx, value); if (js::Object::template is_instance>(ctx->m_ctx, object)) { auto realm_object = get_internal>(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::dict_for_property_array(ctx->m_ctx, ctx->m_object_schema, object); } auto child = realm::Object::create(*ctx, ctx->m_realm, ctx->m_object_schema, static_cast(object), try_update); return child.row(); } }; } // namespace _impl template template U NativeAccessor::unbox(ValueType value, bool create, bool update) { return _impl::Unbox::call(this, std::move(value), create, update); } } // js } // realm