2015-10-27 20:59:15 +00:00
|
|
|
/* Copyright 2015 Realm Inc - All Rights Reserved
|
|
|
|
* Proprietary and Confidential
|
|
|
|
*/
|
2015-08-13 16:12:48 +00:00
|
|
|
|
2015-12-01 22:52:38 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <JavaScriptCore/JSContextRef.h>
|
2015-12-01 22:44:09 +00:00
|
|
|
#include <JavaScriptCore/JSObjectRef.h>
|
2015-12-01 22:52:38 +00:00
|
|
|
#include <JavaScriptCore/JSStringRef.h>
|
2015-12-01 22:44:09 +00:00
|
|
|
|
2015-12-02 03:46:31 +00:00
|
|
|
#include <math.h>
|
2015-08-13 16:12:48 +00:00
|
|
|
#include <string>
|
2015-12-08 20:37:38 +00:00
|
|
|
#include <sstream>
|
2015-12-02 03:46:31 +00:00
|
|
|
|
2015-08-13 16:12:48 +00:00
|
|
|
#include <stdexcept>
|
2015-12-08 20:37:38 +00:00
|
|
|
#include <cmath>
|
2015-10-08 01:42:08 +00:00
|
|
|
#include "property.hpp"
|
2015-09-28 22:47:32 +00:00
|
|
|
#include "schema.hpp"
|
2015-08-13 16:12:48 +00:00
|
|
|
|
2015-12-02 03:42:47 +00:00
|
|
|
|
2015-08-13 16:12:48 +00:00
|
|
|
template<typename T>
|
|
|
|
inline void RJSFinalize(JSObjectRef object) {
|
|
|
|
delete static_cast<T>(JSObjectGetPrivate(object));
|
|
|
|
JSObjectSetPrivate(object, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
inline JSObjectRef RJSWrapObject(JSContextRef ctx, JSClassRef jsClass, T object, JSValueRef prototype = NULL) {
|
|
|
|
JSObjectRef ref = JSObjectMake(ctx, jsClass, (void *)object);
|
|
|
|
if (prototype) {
|
|
|
|
JSObjectSetPrototype(ctx, ref, prototype);
|
|
|
|
}
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
inline T RJSGetInternal(JSObjectRef jsObject) {
|
|
|
|
return static_cast<T>(JSObjectGetPrivate(jsObject));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
2015-10-16 17:35:26 +00:00
|
|
|
JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback getter = NULL, JSObjectSetPropertyCallback setter = NULL, const JSStaticFunction *funcs = NULL,
|
|
|
|
JSObjectGetPropertyNamesCallback propertyNames = NULL) {
|
2015-08-13 16:12:48 +00:00
|
|
|
JSClassDefinition classDefinition = kJSClassDefinitionEmpty;
|
|
|
|
classDefinition.className = name;
|
2015-10-16 17:35:26 +00:00
|
|
|
classDefinition.finalize = RJSFinalize<T>;
|
2015-08-13 16:12:48 +00:00
|
|
|
classDefinition.getProperty = getter;
|
|
|
|
classDefinition.setProperty = setter;
|
|
|
|
classDefinition.staticFunctions = funcs;
|
|
|
|
classDefinition.getPropertyNames = propertyNames;
|
|
|
|
return JSClassCreate(&classDefinition);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string RJSStringForJSString(JSStringRef jsString);
|
2015-10-08 01:42:08 +00:00
|
|
|
std::string RJSStringForValue(JSContextRef ctx, JSValueRef value);
|
2015-08-13 16:12:48 +00:00
|
|
|
std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name = nullptr);
|
|
|
|
|
|
|
|
JSStringRef RJSStringForString(const std::string &str);
|
|
|
|
JSValueRef RJSValueForString(JSContextRef ctx, const std::string &str);
|
|
|
|
|
|
|
|
inline void RJSValidateArgumentCount(size_t argumentCount, size_t expected) {
|
|
|
|
if (argumentCount != expected) {
|
2015-08-28 21:41:30 +00:00
|
|
|
throw std::invalid_argument("Invalid arguments");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void RJSValidateArgumentCountIsAtLeast(size_t argumentCount, size_t expected) {
|
|
|
|
if (argumentCount < expected) {
|
2015-08-13 16:12:48 +00:00
|
|
|
throw std::invalid_argument("Invalid arguments");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void RJSValidateArgumentRange(size_t argumentCount, size_t min, size_t max) {
|
|
|
|
if (argumentCount < min || argumentCount > max) {
|
|
|
|
throw std::invalid_argument("Invalid arguments");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RJSException : public std::runtime_error {
|
|
|
|
public:
|
2015-10-13 06:17:02 +00:00
|
|
|
RJSException(JSContextRef ctx, JSValueRef &ex) : std::runtime_error(RJSStringForValue(ctx, ex)),
|
2015-08-13 16:12:48 +00:00
|
|
|
m_jsException(ex) {}
|
|
|
|
JSValueRef exception() { return m_jsException; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
JSValueRef m_jsException;
|
|
|
|
};
|
|
|
|
|
|
|
|
JSValueRef RJSMakeError(JSContextRef ctx, RJSException &exp);
|
|
|
|
JSValueRef RJSMakeError(JSContextRef ctx, std::exception &exp);
|
|
|
|
JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message);
|
|
|
|
|
2015-10-19 23:46:56 +00:00
|
|
|
bool RJSIsValueArray(JSContextRef ctx, JSValueRef value);
|
2015-11-16 11:26:33 +00:00
|
|
|
bool RJSIsValueArrayBuffer(JSContextRef ctx, JSValueRef value);
|
2015-10-19 23:46:56 +00:00
|
|
|
bool RJSIsValueDate(JSContextRef ctx, JSValueRef value);
|
|
|
|
|
2015-08-13 16:12:48 +00:00
|
|
|
static inline JSObjectRef RJSValidatedValueToObject(JSContextRef ctx, JSValueRef value, const char *message = NULL) {
|
|
|
|
JSObjectRef object = JSValueToObject(ctx, value, NULL);
|
|
|
|
if (!object) {
|
|
|
|
throw std::runtime_error(message ?: "Value is not an object.");
|
|
|
|
}
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2015-10-19 23:46:56 +00:00
|
|
|
static inline JSObjectRef RJSValidatedValueToDate(JSContextRef ctx, JSValueRef value, const char *message = NULL) {
|
|
|
|
JSObjectRef object = JSValueToObject(ctx, value, NULL);
|
|
|
|
if (!object || !RJSIsValueDate(ctx, object)) {
|
|
|
|
throw std::runtime_error(message ?: "Value is not a date.");
|
|
|
|
}
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2015-10-13 00:17:43 +00:00
|
|
|
static inline JSObjectRef RJSValidatedValueToFunction(JSContextRef ctx, JSValueRef value, const char *message = NULL) {
|
|
|
|
JSObjectRef object = JSValueToObject(ctx, value, NULL);
|
|
|
|
if (!object || !JSObjectIsFunction(ctx, object)) {
|
|
|
|
throw std::runtime_error(message ?: "Value is not a function.");
|
|
|
|
}
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2015-08-13 16:12:48 +00:00
|
|
|
static inline double RJSValidatedValueToNumber(JSContextRef ctx, JSValueRef value) {
|
2015-10-22 03:20:51 +00:00
|
|
|
if (JSValueIsNull(ctx, value)) {
|
|
|
|
throw std::invalid_argument("`null` is not a number.");
|
|
|
|
}
|
2015-10-22 03:24:15 +00:00
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
2015-08-13 16:12:48 +00:00
|
|
|
double number = JSValueToNumber(ctx, value, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
2015-10-12 23:50:45 +00:00
|
|
|
if (isnan(number)) {
|
|
|
|
throw std::invalid_argument("Value not convertible to a number.");
|
|
|
|
}
|
2015-08-13 16:12:48 +00:00
|
|
|
return number;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef object, JSStringRef property) {
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
return propertyValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) {
|
|
|
|
JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property);
|
|
|
|
if (JSValueIsUndefined(ctx, propertyValue)) {
|
2015-11-02 19:13:30 +00:00
|
|
|
throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined");
|
2015-08-13 16:12:48 +00:00
|
|
|
}
|
|
|
|
return RJSValidatedValueToObject(ctx, propertyValue, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline JSObjectRef RJSValidatedObjectAtIndex(JSContextRef ctx, JSObjectRef object, unsigned int index) {
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSValueRef objectValue = JSObjectGetPropertyAtIndex(ctx, object, index, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
return RJSValidatedValueToObject(ctx, objectValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property) {
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSValueRef propertyValue = JSObjectGetProperty(ctx, object, property, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
2015-11-02 19:13:30 +00:00
|
|
|
return RJSValidatedStringForValue(ctx, propertyValue, RJSStringForJSString(property).c_str());
|
2015-08-13 16:12:48 +00:00
|
|
|
}
|
|
|
|
|
2015-10-13 22:25:06 +00:00
|
|
|
static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object) {
|
2015-08-13 16:12:48 +00:00
|
|
|
JSValueRef exception = NULL;
|
|
|
|
static JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
|
|
|
|
JSValueRef lengthValue = JSObjectGetProperty(ctx, object, lengthString, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
if (!JSValueIsNumber(ctx, lengthValue)) {
|
|
|
|
throw std::runtime_error("Missing property 'length'");
|
|
|
|
}
|
|
|
|
|
|
|
|
return RJSValidatedValueToNumber(ctx, lengthValue);
|
|
|
|
}
|
|
|
|
|
2015-12-08 20:37:38 +00:00
|
|
|
template<typename T>
|
|
|
|
T stot(const std::string s) {
|
|
|
|
std::istringstream iss(s);
|
|
|
|
T value;
|
|
|
|
iss >> value;
|
|
|
|
if (iss.fail()) {
|
|
|
|
throw std::invalid_argument("Cannot convert string '" + s + "'");
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2015-10-12 22:35:13 +00:00
|
|
|
static inline size_t RJSValidatedPositiveIndex(std::string indexStr) {
|
2015-12-08 20:37:38 +00:00
|
|
|
long index = stot<long>(indexStr);
|
2015-10-12 22:35:13 +00:00
|
|
|
if (index < 0) {
|
|
|
|
throw std::out_of_range(std::string("Index ") + indexStr + " cannot be less than zero.");
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2015-08-13 16:12:48 +00:00
|
|
|
static inline bool RJSIsValueObjectOfType(JSContextRef ctx, JSValueRef value, JSStringRef type) {
|
|
|
|
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
|
|
|
|
|
|
|
|
JSValueRef exception = NULL;
|
|
|
|
JSValueRef constructorValue = JSObjectGetProperty(ctx, globalObject, type, &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = JSValueIsInstanceOfConstructor(ctx, value, RJSValidatedValueToObject(ctx, constructorValue), &exception);
|
|
|
|
if (exception) {
|
|
|
|
throw RJSException(ctx, exception);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|