/* * 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 { namespace basic { template struct object :root { IRCD_OVERLOAD(array) using handle = typename root::handle; using handle_mutable = typename root::handle_mutable; operator JS::Value() const; // for array objects uint32_t size() const; void resize(const uint32_t &); // 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; object(array_t, const size_t &length); object(const JS::HandleValueArray &); object(const value &); object(JSObject *const &); object(JSObject &); object(); }; // Get the JSClass from which the trap can also be derived. template const JSClass &jsclass(const object &); // Private data slot (trap must have flag JSCLASS_HAS_PRIVATE) template T &priv(const object &); template void priv(object &, void *const &); template void priv(object &, const void *const &); template bool is_extensible(const object &); template bool is_array(const object &); template uint32_t size(const object &); template bool deep_freeze(const object &); template bool freeze(const object &); } // namespace basic using object = basic::object; using heap_object = basic::object; using basic::priv; IRCD_STRONG_TYPEDEF(uint, reserved) // // 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(JSObject &obj) :object::root::type{&obj} { } 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(const root &o) :object{o.get()} { } 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 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 { uint32_t ret; if(!JS_GetArrayLength(*cx, handle(*this), &ret)) throw internal_error("Failed to get array object length"); return ret; } template object::operator JS::Value() const { return this->get()? JS::ObjectValue(*this->get()) : JS::NullValue(); } template bool freeze(const object & obj) { return JS_FreezeObject(*cx, obj); } template bool deep_freeze(const object & obj) { return JS_DeepFreezeObject(*cx, obj); } template bool is_array(const object & obj) { bool ret; if(!JS_IsArrayObject(*cx, obj, &ret)) throw internal_error("Failed to query if object is array"); return ret; } template bool is_extensible(const object & obj) { bool ret; if(!JS_IsExtensible(*cx, obj, &ret)) throw internal_error("Failed to query object extensibility"); return ret; } template void priv(object &obj, const void *const &ptr) { priv(obj, const_cast(ptr)); } template void priv(object &obj, void *const &ptr) { JS_SetPrivate(obj, ptr); } template T & priv(const object &obj) { const auto &jsc(jsclass(obj)); const auto ret(JS_GetInstancePrivate(*cx, obj, &jsc, nullptr)); if(!ret) throw error("Object has no private data"); return *reinterpret_cast(ret); } template const JSClass & jsclass(const object &obj) { const auto jsc(JS_GetClass(obj)); if(unlikely(!jsc)) throw error("Object has no JSClass"); return *const_cast(jsc); } } // namespace basic } // namespace js } // namespace ircd