blob: 2af53bdd1e90022e73f4113a12ed1a007dc8369f [file] [log] [blame] [edit]
#pragma once
#if __cplusplus < 201103L
#error Including <emscripten/val.h> requires building with -std=c++11 or newer!
#else
#include <stdint.h> // uintptr_t
#include <emscripten/wire.h>
#include <array>
#include <vector>
namespace emscripten {
class val;
namespace internal {
template<typename WrapperType>
val wrapped_extend(const std::string&, const val&);
// Implemented in JavaScript. Don't call these directly.
extern "C" {
void _emval_register_symbol(const char*);
enum {
_EMVAL_UNDEFINED = 1,
_EMVAL_NULL = 2,
_EMVAL_TRUE = 3,
_EMVAL_FALSE = 4
};
typedef struct _EM_VAL* EM_VAL;
typedef struct _EM_DESTRUCTORS* EM_DESTRUCTORS;
typedef struct _EM_METHOD_CALLER* EM_METHOD_CALLER;
typedef double EM_GENERIC_WIRE_TYPE;
typedef const void* EM_VAR_ARGS;
void _emval_incref(EM_VAL value);
void _emval_decref(EM_VAL value);
void _emval_run_destructors(EM_DESTRUCTORS handle);
EM_VAL _emval_new_array();
EM_VAL _emval_new_object();
EM_VAL _emval_new_cstring(const char*);
EM_VAL _emval_take_value(TYPEID type, EM_VAR_ARGS argv);
EM_VAL _emval_new(
EM_VAL value,
unsigned argCount,
const TYPEID argTypes[],
EM_VAR_ARGS argv);
EM_VAL _emval_get_global(const char* name);
EM_VAL _emval_get_module_property(const char* name);
EM_VAL _emval_get_property(EM_VAL object, EM_VAL key);
void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value);
EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType, EM_DESTRUCTORS* destructors);
bool _emval_equals(EM_VAL first, EM_VAL second);
bool _emval_strictly_equals(EM_VAL first, EM_VAL second);
EM_VAL _emval_call(
EM_VAL value,
unsigned argCount,
const TYPEID argTypes[],
EM_VAR_ARGS argv);
// DO NOT call this more than once per signature. It will
// leak generated function objects!
EM_METHOD_CALLER _emval_get_method_caller(
unsigned argCount, // including return value
const TYPEID argTypes[]);
EM_GENERIC_WIRE_TYPE _emval_call_method(
EM_METHOD_CALLER caller,
EM_VAL handle,
const char* methodName,
EM_DESTRUCTORS* destructors,
EM_VAR_ARGS argv);
void _emval_call_void_method(
EM_METHOD_CALLER caller,
EM_VAL handle,
const char* methodName,
EM_VAR_ARGS argv);
EM_VAL _emval_typeof(EM_VAL value);
}
template<const char* address>
struct symbol_registrar {
symbol_registrar() {
internal::_emval_register_symbol(address);
}
};
template<typename ReturnType, typename... Args>
struct Signature {
/*
typedef typename BindingType<ReturnType>::WireType (*MethodCaller)(
EM_VAL value,
const char* methodName,
EM_DESTRUCTORS* destructors,
typename BindingType<Args>::WireType...);
*/
static EM_METHOD_CALLER get_method_caller() {
static EM_METHOD_CALLER mc = init_method_caller();
return mc;
}
private:
static EM_METHOD_CALLER init_method_caller() {
WithPolicies<>::ArgTypeList<ReturnType, Args...> args;
return _emval_get_method_caller(args.getCount(), args.getTypes());
}
};
struct DestructorsRunner {
public:
explicit DestructorsRunner(EM_DESTRUCTORS d)
: destructors(d)
{}
~DestructorsRunner() {
_emval_run_destructors(destructors);
}
DestructorsRunner(const DestructorsRunner&) = delete;
void operator=(const DestructorsRunner&) = delete;
private:
EM_DESTRUCTORS destructors;
};
template<typename WireType>
struct GenericWireTypeConverter {
static WireType from(double wt) {
return static_cast<WireType>(wt);
}
};
template<typename Pointee>
struct GenericWireTypeConverter<Pointee*> {
static Pointee* from(double wt) {
return reinterpret_cast<Pointee*>(static_cast<uintptr_t>(wt));
}
};
template<typename T>
T fromGenericWireType(double g) {
typedef typename BindingType<T>::WireType WireType;
WireType wt = GenericWireTypeConverter<WireType>::from(g);
return BindingType<T>::fromWireType(wt);
}
template<typename... Args>
struct PackSize;
template<>
struct PackSize<> {
static constexpr size_t value = 0;
};
template<typename Arg, typename... Args>
struct PackSize<Arg, Args...> {
static constexpr size_t value = (sizeof(typename BindingType<Arg>::WireType) + 7) / 8 + PackSize<Args...>::value;
};
union GenericWireType {
union {
unsigned u;
float f;
const void* p;
} w[2];
double d;
};
static_assert(sizeof(GenericWireType) == 8, "GenericWireType must be 8 bytes");
static_assert(alignof(GenericWireType) == 8, "GenericWireType must be 8-byte-aligned");
inline void writeGenericWireType(GenericWireType*& cursor, float wt) {
cursor->w[0].f = wt;
++cursor;
}
inline void writeGenericWireType(GenericWireType*& cursor, double wt) {
cursor->d = wt;
++cursor;
}
template<typename T>
void writeGenericWireType(GenericWireType*& cursor, T* wt) {
cursor->w[0].p = wt;
++cursor;
}
template<typename ElementType>
inline void writeGenericWireType(GenericWireType*& cursor, const memory_view<ElementType>& wt) {
cursor->w[0].u = wt.size;
cursor->w[1].p = wt.data;
++cursor;
}
template<typename T>
void writeGenericWireType(GenericWireType*& cursor, T wt) {
cursor->w[0].u = static_cast<unsigned>(wt);
++cursor;
}
inline void writeGenericWireTypes(GenericWireType*&) {
}
template<typename First, typename... Rest>
EMSCRIPTEN_ALWAYS_INLINE void writeGenericWireTypes(GenericWireType*& cursor, First&& first, Rest&&... rest) {
writeGenericWireType(cursor, BindingType<First>::toWireType(std::forward<First>(first)));
writeGenericWireTypes(cursor, std::forward<Rest>(rest)...);
}
template<typename... Args>
struct WireTypePack {
WireTypePack(Args&&... args) {
GenericWireType* cursor = elements.data();
writeGenericWireTypes(cursor, std::forward<Args>(args)...);
}
operator EM_VAR_ARGS() const {
return elements.data();
}
private:
std::array<GenericWireType, PackSize<Args...>::value> elements;
};
template<typename ReturnType, typename... Args>
struct MethodCaller {
static ReturnType call(EM_VAL handle, const char* methodName, Args&&... args) {
auto caller = Signature<ReturnType, Args...>::get_method_caller();
WireTypePack<Args...> argv(std::forward<Args>(args)...);
EM_DESTRUCTORS destructors;
EM_GENERIC_WIRE_TYPE result = _emval_call_method(
caller,
handle,
methodName,
&destructors,
argv);
DestructorsRunner rd(destructors);
return fromGenericWireType<ReturnType>(result);
}
};
template<typename... Args>
struct MethodCaller<void, Args...> {
static void call(EM_VAL handle, const char* methodName, Args&&... args) {
auto caller = Signature<void, Args...>::get_method_caller();
WireTypePack<Args...> argv(std::forward<Args>(args)...);
_emval_call_void_method(
caller,
handle,
methodName,
argv);
}
};
}
#define EMSCRIPTEN_SYMBOL(name) \
static const char name##_symbol[] = #name; \
static const ::emscripten::internal::symbol_registrar<name##_symbol> name##_registrar
class val {
public:
// missing operators:
// * delete
// * in
// * instanceof
// * ! ~ - + ++ --
// * * / %
// * + -
// * << >> >>>
// * < <= > >=
// * == != === !==
// * & ^ | && || ?:
//
// exposing void, comma, and conditional is unnecessary
// same with: = += -= *= /= %= <<= >>= >>>= &= ^= |=
static val array() {
return val(internal::_emval_new_array());
}
static val object() {
return val(internal::_emval_new_object());
}
static val undefined() {
return val(internal::EM_VAL(internal::_EMVAL_UNDEFINED));
}
static val null() {
return val(internal::EM_VAL(internal::_EMVAL_NULL));
}
static val take_ownership(internal::EM_VAL e) {
return val(e);
}
static val global(const char* name = 0) {
return val(internal::_emval_get_global(name));
}
static val module_property(const char* name) {
return val(internal::_emval_get_module_property(name));
}
template<typename T>
explicit val(T&& value) {
using namespace internal;
typedef internal::BindingType<T> BT;
WireTypePack<T> argv(std::forward<T>(value));
handle = _emval_take_value(
internal::TypeID<T>::get(),
argv);
}
val() = delete;
explicit val(const char* v)
: handle(internal::_emval_new_cstring(v))
{}
val(val&& v)
: handle(v.handle)
{
v.handle = 0;
}
val(const val& v)
: handle(v.handle)
{
internal::_emval_incref(handle);
}
~val() {
internal::_emval_decref(handle);
}
val& operator=(val&& v) {
internal::_emval_decref(handle);
handle = v.handle;
v.handle = 0;
return *this;
}
val& operator=(const val& v) {
internal::_emval_incref(v.handle);
internal::_emval_decref(handle);
handle = v.handle;
return *this;
}
bool hasOwnProperty(const char* key) const {
return val::global("Object")["prototype"]["hasOwnProperty"].call<bool>("call", *this, val(key));
}
bool isNull() const {
return handle == internal::EM_VAL(internal::_EMVAL_NULL);
}
bool isUndefined() const {
return handle == internal::EM_VAL(internal::_EMVAL_UNDEFINED);
}
bool isTrue() const {
return handle == internal::EM_VAL(internal::_EMVAL_TRUE);
}
bool isFalse() const {
return handle == internal::EM_VAL(internal::_EMVAL_FALSE);
}
bool equals(const val& v) const {
return internal::_emval_equals(handle, v.handle);
}
bool strictlyEquals(const val& v) const {
return internal::_emval_strictly_equals(handle, v.handle);
}
template<typename... Args>
val new_(Args&&... args) const {
return internalCall(internal::_emval_new,std::forward<Args>(args)...);
}
template<typename T>
val operator[](const T& key) const {
return val(internal::_emval_get_property(handle, val(key).handle));
}
template<typename K>
void set(const K& key, const val& v) {
internal::_emval_set_property(handle, val(key).handle, v.handle);
}
template<typename K, typename V>
void set(const K& key, const V& value) {
internal::_emval_set_property(handle, val(key).handle, val(value).handle);
}
template<typename... Args>
val operator()(Args&&... args) {
return internalCall(internal::_emval_call, std::forward<Args>(args)...);
}
template<typename ReturnValue, typename... Args>
ReturnValue call(const char* name, Args&&... args) const {
using namespace internal;
return MethodCaller<ReturnValue, Args...>::call(handle, name, std::forward<Args>(args)...);
}
template<typename T, typename ...Policies>
T as(Policies...) const {
using namespace internal;
typedef BindingType<T> BT;
typename WithPolicies<Policies...>::template ArgTypeList<T> targetType;
EM_DESTRUCTORS destructors;
EM_GENERIC_WIRE_TYPE result = _emval_as(
handle,
targetType.getTypes()[0],
&destructors);
DestructorsRunner dr(destructors);
return fromGenericWireType<T>(result);
}
// If code is not being compiled with GNU extensions enabled, typeof() is not a reserved keyword, so support that as a member function.
#if __STRICT_ANSI__
val typeof() const {
return val(_emval_typeof(handle));
}
#endif
// Prefer calling val::typeOf() over val::typeof(), since this form works in both C++11 and GNU++11 build modes. "typeof" is a reserved word in GNU++11 extensions.
val typeOf() const {
return val(_emval_typeof(handle));
}
private:
// takes ownership, assumes handle already incref'd
explicit val(internal::EM_VAL handle)
: handle(handle)
{}
template<typename WrapperType>
friend val internal::wrapped_extend(const std::string& , const val& );
internal::EM_VAL __get_handle() const {
return handle;
}
template<typename Implementation, typename... Args>
val internalCall(Implementation impl, Args&&... args)const {
using namespace internal;
WithPolicies<>::ArgTypeList<Args...> argList;
WireTypePack<Args...> argv(std::forward<Args>(args)...);
return val(
impl(
handle,
argList.getCount(),
argList.getTypes(),
argv));
}
internal::EM_VAL handle;
friend struct internal::BindingType<val>;
};
namespace internal {
template<>
struct BindingType<val> {
typedef internal::EM_VAL WireType;
static WireType toWireType(const val& v) {
_emval_incref(v.handle);
return v.handle;
}
static val fromWireType(WireType v) {
return val::take_ownership(v);
}
};
}
template<typename T>
std::vector<T> vecFromJSArray(val v) {
auto l = v["length"].as<unsigned>();
std::vector<T> rv;
for(unsigned i = 0; i < l; ++i) {
rv.push_back(v[i].as<T>());
}
return rv;
};
}
#endif // ~C++11 version check