2015-10-27 13:59:15 -07:00
|
|
|
/* Copyright 2015 Realm Inc - All Rights Reserved
|
|
|
|
* Proprietary and Confidential
|
|
|
|
*/
|
2015-08-13 09:12:48 -07:00
|
|
|
|
2015-12-01 14:44:09 -08:00
|
|
|
#include "js_util.hpp"
|
|
|
|
#include "js_object.hpp"
|
|
|
|
#include "js_results.hpp"
|
|
|
|
#include "js_schema.hpp"
|
|
|
|
#include "js_list.hpp"
|
|
|
|
#include "js_realm.hpp"
|
2015-08-13 09:12:48 -07:00
|
|
|
|
2015-12-01 14:44:09 -08:00
|
|
|
#include "object_store.hpp"
|
|
|
|
#include "object_accessor.hpp"
|
2015-08-13 09:12:48 -07:00
|
|
|
|
2015-10-06 13:36:56 -06:00
|
|
|
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
|
2015-08-13 09:12:48 -07:00
|
|
|
using namespace realm;
|
|
|
|
|
|
|
|
JSValueRef ObjectGetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef* exception) {
|
2015-10-13 15:25:06 -07:00
|
|
|
try {
|
|
|
|
Object *obj = RJSGetInternal<Object *>(jsObject);
|
|
|
|
return obj->get_property_value<JSValueRef>(ctx, RJSStringForJSString(jsPropertyName));
|
2015-10-27 09:16:19 -07:00
|
|
|
} catch (InvalidPropertyException &ex) {
|
2015-10-27 03:13:21 -07:00
|
|
|
// getters for nonexistent properties in JS should always return undefined
|
2015-10-27 09:16:19 -07:00
|
|
|
} catch (std::exception &ex) {
|
|
|
|
if (exception) {
|
|
|
|
*exception = RJSMakeError(ctx, ex);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
2015-10-27 09:16:19 -07:00
|
|
|
return NULL;
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ObjectSetProperty(JSContextRef ctx, JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef value, JSValueRef* exception) {
|
|
|
|
try {
|
|
|
|
Object *obj = RJSGetInternal<Object *>(jsObject);
|
|
|
|
obj->set_property_value(ctx, RJSStringForJSString(jsPropertyName), value, true);
|
|
|
|
} catch (std::exception &ex) {
|
2015-10-12 02:02:23 -07:00
|
|
|
if (exception) {
|
2015-08-13 09:12:48 -07:00
|
|
|
*exception = RJSMakeError(ctx, ex);
|
|
|
|
}
|
2015-08-14 10:47:56 -07:00
|
|
|
return false;
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-25 10:48:51 -08:00
|
|
|
void ObjectPropertyNames(JSContextRef ctx, JSObjectRef jsObject, JSPropertyNameAccumulatorRef propertyNames) {
|
|
|
|
Object *obj = RJSGetInternal<Object *>(jsObject);
|
|
|
|
|
2016-01-04 18:13:09 -08:00
|
|
|
for (auto &prop : obj->get_object_schema().properties) {
|
2015-11-25 10:48:51 -08:00
|
|
|
JSStringRef propertyName = RJSStringForString(prop.name);
|
|
|
|
JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
|
|
|
|
JSStringRelease(propertyName);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JSClassRef RJSObjectClass() {
|
2015-10-16 10:35:26 -07:00
|
|
|
static JSClassRef s_objectClass = RJSCreateWrapperClass<Object *>("RealmObject", ObjectGetProperty, ObjectSetProperty, NULL, ObjectPropertyNames);
|
2015-08-13 09:12:48 -07:00
|
|
|
return s_objectClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObjectRef RJSObjectCreate(JSContextRef ctx, Object object) {
|
2016-01-04 18:13:09 -08:00
|
|
|
JSValueRef prototype = RJSPrototypes(object.realm().get())[object.get_object_schema().name];
|
2015-08-13 09:12:48 -07:00
|
|
|
JSObjectRef jsObject = RJSWrapObject(ctx, RJSObjectClass(), new Object(object), prototype);
|
|
|
|
return jsObject;
|
|
|
|
}
|
|
|
|
|
2016-01-05 19:33:56 -08:00
|
|
|
extern JSObjectRef RJSDictForPropertyArray(JSContextRef ctx, const ObjectSchema &object_schema, JSObjectRef array);
|
2015-12-08 12:37:38 -08:00
|
|
|
|
|
|
|
namespace realm {
|
|
|
|
|
2015-09-03 14:37:22 -07:00
|
|
|
template<> bool RJSAccessor::dict_has_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) {
|
|
|
|
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
|
2015-11-02 11:59:33 -08:00
|
|
|
JSStringRef propStr = RJSStringForString(prop_name);
|
|
|
|
bool ret = JSObjectHasProperty(ctx, object, propStr);
|
|
|
|
|
|
|
|
JSStringRelease(propStr);
|
|
|
|
return ret;
|
2015-09-03 14:37:22 -07:00
|
|
|
}
|
|
|
|
|
2015-08-13 09:12:48 -07:00
|
|
|
template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) {
|
|
|
|
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
|
2015-11-02 11:59:33 -08:00
|
|
|
JSStringRef propStr = RJSStringForString(prop_name);
|
2015-08-13 09:12:48 -07:00
|
|
|
JSValueRef ex = NULL;
|
|
|
|
JSValueRef ret = JSObjectGetProperty(ctx, object, propStr, &ex);
|
|
|
|
if (ex) {
|
|
|
|
throw RJSException(ctx, ex);
|
|
|
|
}
|
|
|
|
JSStringRelease(propStr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-10-19 13:39:21 -07:00
|
|
|
template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
|
|
|
|
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
|
2015-09-04 15:43:26 -07:00
|
|
|
return defaults.find(prop_name) != defaults.end();
|
|
|
|
}
|
|
|
|
|
2015-10-19 13:39:21 -07:00
|
|
|
template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, Realm *realm, const ObjectSchema &object_schema, const std::string &prop_name) {
|
|
|
|
ObjectDefaults &defaults = RJSDefaults(realm)[object_schema.name];
|
2015-09-04 15:43:26 -07:00
|
|
|
return defaults[prop_name];
|
|
|
|
}
|
|
|
|
|
2015-08-13 09:12:48 -07:00
|
|
|
template<> bool RJSAccessor::is_null(JSContextRef ctx, JSValueRef &val) {
|
2015-11-02 10:08:23 -08:00
|
|
|
return JSValueIsNull(ctx, val) || JSValueIsUndefined(ctx, val);
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::null_value(JSContextRef ctx) {
|
|
|
|
return JSValueMakeNull(ctx);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
template<> bool RJSAccessor::to_bool(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
if (!JSValueIsBoolean(ctx, val)) {
|
|
|
|
throw std::runtime_error("Property expected to be of type boolean");
|
|
|
|
}
|
|
|
|
return JSValueToBoolean(ctx, val);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_bool(JSContextRef ctx, bool b) {
|
|
|
|
return JSValueMakeBoolean(ctx, b);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
template<> long long RJSAccessor::to_long(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
return RJSValidatedValueToNumber(ctx, val);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_long(JSContextRef ctx, long long l) {
|
|
|
|
return JSValueMakeNumber(ctx, l);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
template<> float RJSAccessor::to_float(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
return RJSValidatedValueToNumber(ctx, val);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_float(JSContextRef ctx, float f) {
|
|
|
|
return JSValueMakeNumber(ctx, f);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
template<> double RJSAccessor::to_double(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
return RJSValidatedValueToNumber(ctx, val);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_double(JSContextRef ctx, double d) {
|
|
|
|
return JSValueMakeNumber(ctx, d);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
template<> std::string RJSAccessor::to_string(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
return RJSValidatedStringForValue(ctx, val);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_string(JSContextRef ctx, StringData s) {
|
|
|
|
return RJSValueForString(ctx, s);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
2015-11-16 03:26:33 -08:00
|
|
|
template<> std::string RJSAccessor::to_binary(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
static JSStringRef arrayBufferString = JSStringCreateWithUTF8CString("ArrayBuffer");
|
2015-11-16 13:48:37 -08:00
|
|
|
static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer");
|
|
|
|
static JSStringRef byteLengthString = JSStringCreateWithUTF8CString("byteLength");
|
|
|
|
static JSStringRef byteOffsetString = JSStringCreateWithUTF8CString("byteOffset");
|
2015-11-16 03:26:33 -08:00
|
|
|
static JSStringRef isViewString = JSStringCreateWithUTF8CString("isView");
|
|
|
|
static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array");
|
|
|
|
|
2015-11-16 13:48:37 -08:00
|
|
|
JSObjectRef arrayBufferConstructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), arrayBufferString);
|
|
|
|
JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString);
|
|
|
|
JSValueRef uint8ArrayArguments[3];
|
|
|
|
size_t uint8ArrayArgumentsCount = 0;
|
|
|
|
|
|
|
|
// Value should either be an ArrayBuffer or ArrayBufferView (i.e. TypedArray or DataView).
|
|
|
|
if (JSValueIsInstanceOfConstructor(ctx, val, arrayBufferConstructor, NULL)) {
|
|
|
|
uint8ArrayArguments[0] = val;
|
|
|
|
uint8ArrayArgumentsCount = 1;
|
|
|
|
}
|
|
|
|
else if (JSObjectRef object = JSValueToObject(ctx, val, NULL)) {
|
|
|
|
// Check if value is an ArrayBufferView by calling ArrayBuffer.isView(val).
|
|
|
|
JSObjectRef isViewMethod = RJSValidatedObjectProperty(ctx, arrayBufferConstructor, isViewString);
|
|
|
|
JSValueRef isView = JSObjectCallAsFunction(ctx, isViewMethod, arrayBufferConstructor, 1, &val, NULL);
|
|
|
|
|
|
|
|
if (isView && JSValueToBoolean(ctx, isView)) {
|
|
|
|
uint8ArrayArguments[0] = RJSValidatedObjectProperty(ctx, object, bufferString);
|
|
|
|
uint8ArrayArguments[1] = RJSValidatedPropertyValue(ctx, object, byteOffsetString);
|
|
|
|
uint8ArrayArguments[2] = RJSValidatedPropertyValue(ctx, object, byteLengthString);
|
|
|
|
uint8ArrayArgumentsCount = 3;
|
2015-11-16 03:26:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 13:48:37 -08:00
|
|
|
if (!uint8ArrayArgumentsCount) {
|
|
|
|
throw std::runtime_error("Can only convert ArrayBuffer and TypedArray objects to binary");
|
|
|
|
}
|
2015-11-16 03:26:33 -08:00
|
|
|
|
2015-11-16 13:48:37 -08:00
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, uint8ArrayArgumentsCount, uint8ArrayArguments, &exception);
|
2015-11-16 03:26:33 -08:00
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t byteCount = RJSValidatedListLength(ctx, uint8Array);
|
|
|
|
std::string bytes(byteCount, 0);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < byteCount; i++) {
|
|
|
|
JSValueRef byteValue = JSObjectGetPropertyAtIndex(ctx, uint8Array, (unsigned)i, NULL);
|
|
|
|
bytes[i] = JSValueToNumber(ctx, byteValue, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
template<> JSValueRef RJSAccessor::from_binary(JSContextRef ctx, BinaryData data) {
|
|
|
|
static JSStringRef bufferString = JSStringCreateWithUTF8CString("buffer");
|
|
|
|
static JSStringRef uint8ArrayString = JSStringCreateWithUTF8CString("Uint8Array");
|
|
|
|
|
|
|
|
size_t byteCount = data.size();
|
|
|
|
JSValueRef byteCountValue = JSValueMakeNumber(ctx, byteCount);
|
|
|
|
JSObjectRef uint8ArrayContructor = RJSValidatedObjectProperty(ctx, JSContextGetGlobalObject(ctx), uint8ArrayString);
|
|
|
|
JSObjectRef uint8Array = JSObjectCallAsConstructor(ctx, uint8ArrayContructor, 1, &byteCountValue, NULL);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < byteCount; i++) {
|
|
|
|
JSValueRef num = JSValueMakeNumber(ctx, data[i]);
|
|
|
|
JSObjectSetPropertyAtIndex(ctx, uint8Array, (unsigned)i, num, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return RJSValidatedObjectProperty(ctx, uint8Array, bufferString);
|
|
|
|
}
|
|
|
|
|
2015-08-13 09:12:48 -07:00
|
|
|
template<> DateTime RJSAccessor::to_datetime(JSContextRef ctx, JSValueRef &val) {
|
2015-10-19 16:46:56 -07:00
|
|
|
JSObjectRef object = RJSValidatedValueToDate(ctx, val);
|
|
|
|
double utc = RJSValidatedValueToNumber(ctx, object);
|
2015-08-13 09:12:48 -07:00
|
|
|
|
|
|
|
return DateTime(utc);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_datetime(JSContextRef ctx, DateTime dt) {
|
|
|
|
JSValueRef time = JSValueMakeNumber(ctx, dt.get_datetime());
|
|
|
|
return JSObjectMakeDate(ctx, 1, &time, NULL);
|
|
|
|
}
|
2015-08-13 09:12:48 -07:00
|
|
|
|
2015-11-10 15:58:04 -08:00
|
|
|
template<> size_t RJSAccessor::to_existing_object_index(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
JSObjectRef object = RJSValidatedValueToObject(ctx, val);
|
|
|
|
if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) {
|
|
|
|
return RJSGetInternal<Object *>(object)->row().get_index();
|
|
|
|
}
|
|
|
|
throw std::runtime_error("object is not a Realm Object");
|
|
|
|
}
|
2015-10-26 15:27:43 -07:00
|
|
|
template<> size_t RJSAccessor::to_object_index(JSContextRef ctx, SharedRealm realm, JSValueRef &val, const std::string &type, bool try_update) {
|
2015-08-13 09:12:48 -07:00
|
|
|
JSObjectRef object = RJSValidatedValueToObject(ctx, val);
|
|
|
|
if (JSValueIsObjectOfClass(ctx, val, RJSObjectClass())) {
|
2015-10-26 15:27:43 -07:00
|
|
|
return RJSGetInternal<Object *>(object)->row().get_index();
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
|
2015-09-09 17:16:32 -07:00
|
|
|
auto object_schema = realm->config().schema->find(type);
|
2015-08-14 09:47:33 -07:00
|
|
|
if (RJSIsValueArray(ctx, object)) {
|
2015-09-09 17:16:32 -07:00
|
|
|
object = RJSDictForPropertyArray(ctx, *object_schema, object);
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
|
2015-09-09 17:16:32 -07:00
|
|
|
Object child = Object::create<JSValueRef>(ctx, realm, *object_schema, (JSValueRef)object, try_update);
|
2015-10-26 15:27:43 -07:00
|
|
|
return child.row().get_index();
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_object(JSContextRef ctx, Object object) {
|
|
|
|
return RJSObjectCreate(ctx, object);
|
2015-08-13 09:12:48 -07:00
|
|
|
}
|
|
|
|
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> size_t RJSAccessor::list_size(JSContextRef ctx, JSValueRef &val) {
|
|
|
|
return RJSValidatedListLength(ctx, RJSValidatedValueToObject(ctx, val));
|
|
|
|
}
|
|
|
|
template<> JSValueRef RJSAccessor::list_value_at_index(JSContextRef ctx, JSValueRef &val, size_t index) {
|
2015-08-13 09:12:48 -07:00
|
|
|
return RJSValidatedObjectAtIndex(ctx, RJSValidatedValueToObject(ctx, val), (unsigned int)index);
|
|
|
|
}
|
2015-10-13 15:25:06 -07:00
|
|
|
template<> JSValueRef RJSAccessor::from_list(JSContextRef ctx, List list) {
|
2015-10-13 15:27:24 -07:00
|
|
|
return RJSListCreate(ctx, list);
|
2015-10-13 15:25:06 -07:00
|
|
|
}
|
2015-12-08 12:37:38 -08:00
|
|
|
|
|
|
|
}
|