/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #pragma once #define HAVE_IRCD_JS_OBJECT_H namespace ircd { namespace js { using object_handle = JS::Handle; using object_handle_mutable = JS::MutableHandle; // get()/set() et al overload on reserved{n} to manipulate reserved slot n IRCD_STRONG_TYPEDEF(uint, reserved) // Get the JSClass from which the trap can also be derived. bool has_jsclass(const JSObject *const &); const JSClass &jsclass(JSObject *const &); // Get the flags from the object's JSClass or 0. uint flags(const JSObject *const &); // Get the `this` global from any object JSObject *current_global(JSObject *const &); // Misc utils bool is_extensible(const object_handle &); bool is_array(const object_handle &); uint32_t size(const object_handle &); bool deep_freeze(const object_handle &); bool freeze(const object_handle &); namespace basic { template struct object :root { IRCD_OVERLOAD(json) IRCD_OVERLOAD(array) IRCD_OVERLOAD(uninitialized) using handle = object_handle; using handle_mutable = object_handle_mutable; explicit operator JSString *() const; operator JS::Value() const; // for array objects uint32_t size() const; void resize(const uint32_t &); // Get/set prototype object prototype() const; void prototype(object::handle); // Get the constructor object constructor() const; // new object object(const JSClass *const &, const handle &ctor, const JS::HandleValueArray &args); object(const JSClass *const &, const JS::CallArgs &args); object(const JSClass *const &, const object &proto); object(const JSClass *const &); template object(const root &); using root::root; template object(json_t, args&&...); object(array_t, const size_t &length); object(const JS::HandleValueArray &); object(const value &); object(JSObject *const &); object(JSObject &); object(uninitialized_t); object(); static object global(); // current_global(cx) }; } // namespace basic using object = basic::object; using heap_object = basic::object; using persist_object = basic::object; // // Implementation // namespace basic { template object::object() :object::root::type { JS_NewPlainObject(*cx) } { if(unlikely(!this->get())) throw internal_error("NULL object (plain)"); } template object::object(uninitialized_t) :object::root::type{} { } template object::object(JSObject &obj) :object::root::type{&obj} { if(unlikely(!this->get())) throw internal_error("NULL object (ref)"); } template object::object(JSObject *const &obj) :object::root::type{obj} { if(unlikely(!this->get())) throw internal_error("NULL object"); } template object::object(const value &val) :object::root::type{} { if(!JS_ValueToObject(*cx, val, &(*this))) throw type_error("Value is not an Object"); } template object::object(const JS::HandleValueArray &values) :object::root::type { JS_NewArrayObject(*cx, values) } { if(unlikely(!this->get())) throw internal_error("NULL object (array)"); } template object::object(array_t, const size_t &length) :object::root::type { JS_NewArrayObject(*cx, length) } { if(unlikely(!this->get())) throw internal_error("NULL object (array)"); } template template object::object(json_t, args&&... a) :object(js::json::parse(std::forward(a)...)) { } template template object::object(const root &o) :object{o.get()} { if(unlikely(!this->get())) throw internal_error("NULL object (cross-lifetime)"); } template object::object(const JSClass *const &clasp) :object::root::type { JS_NewObject(*cx, clasp) } { if(unlikely(!this->get())) throw internal_error("NULL object (clasp)"); } template object::object(const JSClass *const &clasp, const object &proto) :object::root::type { JS_NewObjectWithGivenProto(*cx, clasp, proto) } { if(unlikely(!this->get())) throw internal_error("NULL object (with given proto)"); } template object::object(const JSClass *const &clasp, const JS::CallArgs &args) :object::root::type { JS_NewObjectForConstructor(*cx, clasp, args) } { if(unlikely(!this->get())) throw internal_error("NULL object (for constructor)"); } template object::object(const JSClass *const &clasp, const object::handle &ctor, const JS::HandleValueArray &args) :object::root::type { JS_New(*cx, ctor, args) } { if(unlikely(!this->get())) throw internal_error("NULL object (new)"); } template object object::global() { return current_global(); } template object object::constructor() const { return JS_GetConstructor(*cx, *this); } template void object::prototype(object::handle obj) { if(!JS_SetPrototype(*cx, *this, obj)) throw internal_error("Failed to set prototype for object"); } template object object::prototype() const { object ret; if(!JS_GetPrototype(*cx, *this, &ret)) throw internal_error("Failed to get prototype for object"); return ret; } template void object::resize(const uint32_t &length) { if(!JS_SetArrayLength(*cx, *this, length)) throw internal_error("Failed to set array object length"); } template uint32_t object::size() const { return js::size(*this); } template object::operator JSString *() const { assert(this->get()); return JS_BasicObjectToString(*cx, *this); } template object::operator JS::Value() const { assert(this->get()); return JS::ObjectValue(*this->get()); } } // namespace basic inline bool freeze(const object_handle &obj) { return JS_FreezeObject(*cx, obj); } inline bool deep_freeze(const object_handle &obj) { return JS_DeepFreezeObject(*cx, obj); } inline uint32_t size(const object_handle &obj) { uint32_t ret; if(!JS_GetArrayLength(*cx, obj, &ret)) throw internal_error("Failed to get array object length"); return ret; } inline bool is_array(const object_handle &obj) { bool ret; if(!JS_IsArrayObject(*cx, obj, &ret)) throw internal_error("Failed to query if object is array"); return ret; } inline bool is_extensible(const object_handle &obj) { bool ret; if(!JS_IsExtensible(*cx, obj, &ret)) throw internal_error("Failed to query object extensibility"); return ret; } inline JSObject * current_global(JSObject *const &obj) { return JS_GetGlobalForObject(*cx, obj); } inline uint flags(const JSObject *const &obj) { return has_jsclass(obj)? jsclass(const_cast(obj)).flags : 0; } inline const JSClass & jsclass(JSObject *const &obj) { assert(has_jsclass(obj)); const auto jsc(JS_GetClass(obj)); return *const_cast(jsc); } inline bool has_jsclass(const JSObject *const &obj) { assert(obj); return JS_GetClass(const_cast(obj)) != nullptr; } } // namespace js } // namespace ircd