0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 16:22:35 +01:00

ircd::js: Convert all JS type wrappers into templates and typedef on requsted GC.

Templates are now in the namespace basic:: i.e `basic::value<>`
Typedefs are in ircd::js:: matching the original non-template types.

Ex. stack-duration type in template form is: `basic::value<lifetime::stack>`
Ex. stack-duration type in typedef form is `value`
Ex. heap-duration type in template form is: `basic::value<lifetime::heap>`
Ex. heap-duration type in typedef form is: `heap_value`
This commit is contained in:
Jason Volk 2016-10-27 01:55:26 -07:00
parent 7e66df914a
commit 6b5bab0871
11 changed files with 906 additions and 636 deletions

View file

@ -30,18 +30,19 @@ string display_name(const JSFunction &);
string name(const JSFunction &);
uint16_t arity(const JSFunction &f);
namespace basic {
template<lifetime L>
struct function
:JS::Rooted<JSFunction *>
:root<JSFunction *, L>
{
using handle = JS::HandleFunction;
using handle_mutable = JS::MutableHandleFunction;
operator JSObject *() const;
explicit operator script() const;
explicit operator string() const;
explicit operator script<L>() const;
explicit operator string<L>() const;
value operator()(const object &, const JS::HandleValueArray &args) const;
value operator()(const object &) const;
// js::value/js::object == lifetime::stack
js::value operator()(const js::object &, const JS::HandleValueArray &args) const;
js::value operator()(const js::object &) const;
// new function
function(JS::AutoObjectVector &stack,
@ -50,89 +51,125 @@ struct function
const std::vector<std::string> &args,
const std::string &src);
explicit function(const value &);
using root<JSFunction *, L>::root;
explicit function(const value<L> &);
function(JSFunction *const &);
function(JSFunction &);
function();
function(function &&) noexcept;
function(const function &) = delete;
function &operator=(function &&) noexcept;
};
inline
function::function()
:JS::Rooted<JSFunction *>{*cx}
} // namespace basic
using function = basic::function<lifetime::stack>;
using heap_function = basic::function<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
function<L>::function(JSFunction &func)
:function<L>::root::type{&func}
{
}
inline
function::function(function &&other)
noexcept
:JS::Rooted<JSFunction *>{*cx, other}
template<lifetime L>
function<L>::function(JSFunction *const &func)
:function<L>::root::type{func}
{
other.set(nullptr);
}
inline function &
function::operator=(function &&other)
noexcept
{
set(other.get());
other.set(nullptr);
return *this;
}
inline
function::function(JSFunction &func)
:JS::Rooted<JSFunction *>{*cx, &func}
{
}
inline
function::function(JSFunction *const &func)
:JS::Rooted<JSFunction *>{*cx, func}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw internal_error("NULL function");
}
inline
function::function(const value &val)
:JS::Rooted<JSFunction *>
template<lifetime L>
function<L>::function(const value<L> &val)
:function<L>::root::type
{
*cx,
JS_ValueToFunction(*cx, val)
}
{
if(!get())
if(!this->get())
throw type_error("value is not a function");
}
inline
function::operator string()
template<lifetime L>
function<L>::function(JS::AutoObjectVector &stack,
const JS::CompileOptions &opts,
const char *const &name,
const std::vector<std::string> &args,
const std::string &src)
:function<L>::root::type{}
{
std::vector<const char *> argp(args.size());
std::transform(begin(args), end(args), begin(argp), []
(const std::string &arg)
{
return arg.data();
});
if(!JS::CompileFunction(*cx,
stack,
opts,
name,
argp.size(),
&argp.front(),
src.data(),
src.size(),
&(*this)))
{
throw syntax_error("Failed to compile function");
}
}
template<lifetime L>
js::value
function<L>::operator()(const js::object &that)
const
{
return this->operator()(that, JS::HandleValueArray::empty());
}
template<lifetime L>
js::value
function<L>::operator()(const js::object &that,
const JS::HandleValueArray &args)
const
{
js::value ret;
if(!JS_CallFunction(*cx, that, *this, args, &ret))
throw jserror(jserror::pending);
return ret;
}
template<lifetime L>
function<L>::operator string<L>()
const
{
return decompile(*this, true);
}
inline
function::operator script()
template<lifetime L>
function<L>::operator script<L>()
const
{
return JS_GetFunctionScript(*cx, *this);
}
inline
function::operator JSObject *()
template<lifetime L>
function<L>::operator JSObject *()
const
{
const auto ret(JS_GetFunctionObject(get()));
const auto ret(JS_GetFunctionObject(this->get()));
if(unlikely(!ret))
throw type_error("function cannot cast to Object");
return ret;
}
} // namespace basic
inline uint16_t
arity(const JSFunction &f)
{

View file

@ -26,7 +26,7 @@ namespace ircd {
namespace js {
class function_literal
:public JS::PersistentRooted<JSFunction *>
:public root<JSFunction *, lifetime::persist>
{
const char *name;
const char *text;

View file

@ -24,131 +24,123 @@
namespace ircd {
namespace js {
namespace basic {
template<lifetime L>
struct id
:JS::Rooted<jsid>
:root<jsid, L>
{
using handle = JS::HandleId;
using handle_mutable = JS::MutableHandleId;
explicit id(const char *const &); // creates new id (permanent)
explicit id(const std::string &); // creates new id (permanent)
id(const string::handle &);
id(const value::handle &);
using root<jsid, L>::root;
explicit id(const char *const &); // creates new id
explicit id(const std::string &); // creates new id
id(const typename string<L>::handle &);
id(const typename value<L>::handle &);
id(const JSProtoKey &);
id(const uint32_t &);
id(const handle_mutable &);
id(const handle &);
id(const jsid &);
id();
id(id &&) noexcept;
id(const id &) = delete;
friend bool operator==(const id &, const char *const &);
friend bool operator==(const id &, const std::string &);
friend bool operator==(const char *const &, const id &);
friend bool operator==(const std::string &, const id &);
};
inline
id::id()
:JS::Rooted<jsid>{*cx}
template<lifetime L> bool operator==(const id<L> &, const char *const &);
template<lifetime L> bool operator==(const id<L> &, const std::string &);
template<lifetime L> bool operator==(const char *const &, const id<L> &);
template<lifetime L> bool operator==(const std::string &, const id<L> &);
} // namespace basic
using id = basic::id<lifetime::stack>;
using heap_id = basic::id<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
id<L>::id()
:id<L>::root::type{}
{
}
inline
id::id(id &&other)
noexcept
:JS::Rooted<jsid>{*cx, other}
template<lifetime L>
id<L>::id(const jsid &i)
:id<L>::root::type{i}
{
}
inline
id::id(const jsid &i)
:JS::Rooted<jsid>{*cx, i}
{
}
inline
id::id(const handle &h)
:JS::Rooted<jsid>{*cx, h}
{
}
inline
id::id(const handle_mutable &h)
:JS::Rooted<jsid>{*cx, h}
{
}
inline
id::id(const uint32_t &index)
:JS::Rooted<jsid>{*cx}
template<lifetime L>
id<L>::id(const uint32_t &index)
:id<L>::root::type{}
{
if(!JS_IndexToId(*cx, index, &(*this)))
throw type_error("Failed to construct id from uint32_t index");
}
inline
id::id(const JSProtoKey &key)
:JS::Rooted<jsid>{*cx}
template<lifetime L>
id<L>::id(const JSProtoKey &key)
:id<L>::root::type{}
{
JS::ProtoKeyToId(*cx, key, &(*this));
}
inline
id::id(const value::handle &h)
:JS::Rooted<jsid>{*cx}
template<lifetime L>
id<L>::id(const std::string &str)
:id(str.c_str())
{
}
template<lifetime L>
id<L>::id(const char *const &str)
:id<L>::root::type{jsid()}
{
if(!JS::PropertySpecNameToPermanentId(*cx, str, this->address()))
throw type_error("Failed to create id from native string");
}
template<lifetime L>
id<L>::id(const typename value<L>::handle &h)
:id<L>::root::type{}
{
if(!JS_ValueToId(*cx, h, &(*this)))
throw type_error("Failed to construct id from Value");
}
inline
id::id(const string::handle &h)
:JS::Rooted<jsid>{*cx}
template<lifetime L>
id<L>::id(const typename string<L>::handle &h)
:id<L>::root::type{}
{
if(!JS_StringToId(*cx, h, &(*this)))
throw type_error("Failed to construct id from String");
}
inline
id::id(const std::string &str)
:id(str.c_str())
{
}
inline
id::id(const char *const &str)
:JS::Rooted<jsid>{*cx, jsid()}
{
if(!JS::PropertySpecNameToPermanentId(*cx, str, address()))
throw type_error("Failed to create id from native string");
}
inline bool
operator==(const std::string &a, const id &b)
template<lifetime L>
bool
operator==(const std::string &a, const id<L> &b)
{
return operator==(a.c_str(), b);
}
inline bool
operator==(const char *const &a, const id &b)
template<lifetime L>
bool
operator==(const char *const &a, const id<L> &b)
{
return JS::PropertySpecNameEqualsId(a, b);
}
inline bool
operator==(const id &a, const std::string &b)
template<lifetime L>
bool
operator==(const id<L> &a, const std::string &b)
{
return operator==(a, b.c_str());
}
inline bool
operator==(const id &a, const char *const &b)
template<lifetime L>
bool
operator==(const id<L> &a, const char *const &b)
{
return JS::PropertySpecNameEqualsId(b, a);
}
} // namespace basic
} // namespace js
} // namespace ircd

View file

@ -67,6 +67,7 @@ inline JSVersion version(const char *const &v) { return JS_StringToVersion(v);
#include "debug.h"
#include "error.h"
#include "native.h"
#include "root.h"
#include "value.h"
#include "string.h"
#include "id.h"

View file

@ -24,12 +24,14 @@
namespace ircd {
namespace js {
namespace basic {
template<lifetime L>
struct object
:JS::Rooted<JSObject *>
:root<JSObject *, L>
{
using handle = JS::HandleObject;
using handle_mutable = JS::MutableHandleObject;
using handle = typename root<JSObject *, L>::handle;
using handle_mutable = typename root<JSObject *, L>::handle_mutable;
operator JS::Value() const;
@ -39,116 +41,110 @@ struct object
object(const JSClass *const &, const object &proto);
object(const JSClass *const &);
object(const object::handle_mutable &h): object{h.get()} {}
object(const object::handle &h): object{h.get()} {}
explicit object(const value &);
template<class T, lifetime LL> object(const root<T, LL> &);
using root<JSObject *, L>::root;
object(const value<L> &);
object(JSObject *const &);
object(JSObject &);
object();
object(object &&) noexcept;
object(const object &) = delete;
object &operator=(object &&) noexcept;
};
inline
object::object()
:JS::Rooted<JSObject *>
} // namespace basic
using object = basic::object<lifetime::stack>;
using heap_object = basic::object<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
object<L>::object()
:object<L>::root::type
{
*cx,
JS_NewPlainObject(*cx)
}
{
}
inline
object::object(object &&other)
noexcept
:JS::Rooted<JSObject *>{*cx, other}
{
other.set(nullptr);
}
inline object &
object::operator=(object &&other)
noexcept
{
set(other.get());
other.set(nullptr);
return *this;
}
inline
object::object(JSObject &obj)
:JS::Rooted<JSObject *>{*cx, &obj}
template<lifetime L>
object<L>::object(JSObject &obj)
:object<L>::root::type{&obj}
{
}
inline
object::object(JSObject *const &obj)
:JS::Rooted<JSObject *>{*cx, obj}
template<lifetime L>
object<L>::object(JSObject *const &obj)
:object<L>::root::type{obj}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw internal_error("NULL object");
}
inline
object::object(const value &val)
:JS::Rooted<JSObject *>{*cx}
template<lifetime L>
object<L>::object(const value<L> &val)
:object<L>::root::type{}
{
if(!JS_ValueToObject(*cx, val, &(*this)))
throw type_error("Value is not an Object");
}
inline
object::object(const JSClass *const &clasp)
:JS::Rooted<JSObject *>
template<lifetime L>
template<class T,
lifetime LL>
object<L>::object(const root<T, LL> &o)
:object{o.get()}
{
}
template<lifetime L>
object<L>::object(const JSClass *const &clasp)
:object<L>::root::type
{
*cx,
JS_NewObject(*cx, clasp)
}
{
}
inline
object::object(const JSClass *const &clasp,
template<lifetime L>
object<L>::object(const JSClass *const &clasp,
const object &proto)
:JS::Rooted<JSObject *>
:object<L>::root::type
{
*cx,
JS_NewObjectWithGivenProto(*cx, clasp, proto)
}
{
}
inline
object::object(const JSClass *const &clasp,
template<lifetime L>
object<L>::object(const JSClass *const &clasp,
const JS::CallArgs &args)
:JS::Rooted<JSObject *>
:object<L>::root::type
{
*cx,
JS_NewObjectForConstructor(*cx, clasp, args)
}
{
}
inline
object::object(const JSClass *const &clasp,
template<lifetime L>
object<L>::object(const JSClass *const &clasp,
const object::handle &ctor,
const JS::HandleValueArray &args)
:JS::Rooted<JSObject *>
:object<L>::root::type
{
*cx,
JS_New(*cx, ctor, args)
}
{
}
inline
object::operator JS::Value()
template<lifetime L>
object<L>::operator JS::Value()
const
{
return get()? JS::ObjectValue(*get()) : JS::NullValue();
return this->get()? JS::ObjectValue(*this->get()) : JS::NullValue();
}
} // namespace basic
} // namespace js
} // namespace ircd

267
include/ircd/js/root.h Normal file
View file

@ -0,0 +1,267 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* 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_ROOT_H
namespace ircd {
namespace js {
enum class lifetime
{
stack, // entity lives on the stack and the GC uses a cheap forward list between objects
heap, // entity lives on the heap with dynamic duration
tenured, // long-life-optimized heap entity with special rules (must read jsapi docs)
persist, // entity has duration similar to the runtime itself
maybe, // template with a boolean switch for GC participation
fake, // noop; does not register with GC at all
};
template<class T,
lifetime L>
struct root
{
};
template<class T>
struct root<T, lifetime::stack>
:JS::Rooted<T>
{
using base_type = JS::Rooted<T>;
using type = root<T, lifetime::stack>;
using handle_mutable = JS::MutableHandle<T>;
using handle = JS::Handle<T>;
root(const handle &h)
:JS::Rooted<T>{*cx, h}
{
}
root(const handle_mutable &h)
:JS::Rooted<T>{*cx, h}
{
}
template<lifetime L>
root(const root<T, L> &other)
:JS::Rooted<T>{*cx, other.get()}
{
}
explicit root(const T &t)
:JS::Rooted<T>{*cx, t}
{
}
root()
:JS::Rooted<T>{*cx}
{
}
root(root&& other)
noexcept
:JS::Rooted<T>{*cx, other.get()}
{
}
root &operator=(root &&other)
noexcept
{
this->set(other.get());
return *this;
}
root(const root &) = delete;
root &operator=(const root &) = delete;
};
template<class T>
struct root<T, lifetime::heap>
:JS::Heap<T>
{
using type = root<T, lifetime::heap>;
using handle = JS::Handle<T>;
using handle_mutable = JS::MutableHandle<T>;
operator handle() const
{
return JS::Handle<T>::fromMarkedLocation(this->address());
}
operator handle_mutable()
{
const auto ptr(const_cast<T *>(this->address()));
return JS::MutableHandle<T>::fromMarkedLocation(ptr);
}
auto operator&() const
{
return static_cast<handle>(*this);
}
auto operator&()
{
return static_cast<handle_mutable>(*this);
}
root(const handle &h)
:JS::Heap<T>{h}
{
}
root(const handle_mutable &h)
:JS::Heap<T>{h}
{
}
template<lifetime L>
root(const root<T, L> &other)
:JS::Heap<T>{other.get()}
{
}
explicit root(const T &t)
:JS::Heap<T>{t}
{
}
root()
:JS::Heap<T>{}
{
}
root(root&&) = default;
root(const root &) = default;
root &operator=(root &&) = default;
root &operator=(const root &) = default;
};
template<class T>
struct root<T, lifetime::tenured>
:JS::TenuredHeap<T>
{
using type = root<T, lifetime::tenured>;
using handle_mutable = JS::MutableHandle<T>;
using handle = JS::Handle<T>;
operator handle() const
{
return JS::Handle<T>::fromMarkedLocation(this->address());
}
operator handle_mutable()
{
const auto ptr(const_cast<T *>(this->address()));
return JS::MutableHandle<T>::fromMarkedLocation(ptr);
}
auto operator&() const
{
return static_cast<handle>(*this);
}
auto operator&()
{
return static_cast<handle_mutable>(*this);
}
root(const handle &h)
:JS::TenuredHeap<T>{h}
{
}
root(const handle_mutable &h)
:JS::TenuredHeap<T>{h}
{
}
template<lifetime L>
root(const root<T, L> &other)
:JS::TenuredHeap<T>{other.get()}
{
}
explicit root(const T &t)
:JS::TenuredHeap<T>{t}
{
}
root()
:JS::TenuredHeap<T>{}
{
}
root(root&&) = default;
root(const root &) = default;
root &operator=(root &&) = default;
root &operator=(const root &) = default;
};
template<class T>
struct root<T, lifetime::persist>
:JS::PersistentRooted<T>
{
using type = root<T, lifetime::persist>;
using handle_mutable = JS::MutableHandle<T>;
using handle = JS::Handle<T>;
template<lifetime L>
root(const root<T, L> &other)
:JS::PersistentRooted<T>{*cx, other.get()}
{
}
explicit root(const T &t)
:JS::PersistentRooted<T>{*cx, t}
{
}
root()
:JS::PersistentRooted<T>{*cx}
{
}
root(root&& other)
noexcept
:JS::PersistentRooted<T>{*cx, other.get()}
{
}
root(const root &) = delete;
root &operator=(const root &) = delete;
};
template<class T>
struct root<T, lifetime::maybe>
:public ::js::MaybeRooted<T, ::js::CanGC>
{
using type = root<T, lifetime::maybe>;
};
template<class T>
struct root<T, lifetime::fake>
:public ::js::FakeRooted<T>
{
using type = root<T, lifetime::fake>;
};
} // namespace js
} // namespace ircd

View file

@ -24,50 +24,78 @@
namespace ircd {
namespace js {
namespace basic {
template<lifetime L>
struct script
:JS::Rooted<JSScript *>
:root<JSScript *, L>
{
using handle = JS::HandleScript;
using handle_mutable = JS::MutableHandleScript;
value operator()(JS::AutoObjectVector &stack) const;
value operator()() const;
value<lifetime::stack> operator()(JS::AutoObjectVector &stack) const;
value<lifetime::stack> operator()() const;
using root<JSScript *, L>::root;
script(const JS::CompileOptions &opts, const std::string &src); // new script
script(JSScript *const &);
script(JSScript &);
script();
script(script &&) noexcept;
script(const script &) = delete;
};
inline
script::script()
:JS::Rooted<JSScript *>{*cx}
} // namespace basic
using script = basic::script<lifetime::stack>;
using heap_script = basic::script<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
script<L>::script(JSScript &val)
:script<L>::root::type{&val}
{
}
inline
script::script(script &&other)
noexcept
:JS::Rooted<JSScript *>{*cx, other}
template<lifetime L>
script<L>::script(JSScript *const &val)
:script<L>::root::type{val}
{
}
inline
script::script(JSScript &val)
:JS::Rooted<JSScript *>{*cx, &val}
{
}
inline
script::script(JSScript *const &val)
:JS::Rooted<JSScript *>{*cx, val}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw internal_error("NULL script");
}
template<lifetime L>
script<L>::script(const JS::CompileOptions &opts,
const std::string &src)
:script<L>::root::type{}
{
if(!JS::Compile(*cx, opts, src.data(), src.size(), &(*this)))
throw syntax_error("Failed to compile script");
}
template<lifetime L>
value<lifetime::stack>
script<L>::operator()()
const
{
value<lifetime::stack> ret;
if(!JS_ExecuteScript(*cx, *this, &ret))
throw jserror(jserror::pending);
return ret;
}
template<lifetime L>
value<lifetime::stack>
script<L>::operator()(JS::AutoObjectVector &stack)
const
{
value<lifetime::stack> ret;
if(!JS_ExecuteScript(*cx, stack, *this, &ret))
throw jserror(jserror::pending);
return ret;
}
} // namespace basic
} // namespace js
} // namespace ircd

View file

@ -29,25 +29,28 @@ bool external(const JSString *const &);
size_t size(const JSString *const &);
char16_t at(const JSString *const &, const size_t &);
const size_t CSTR_BUFS = 8;
const size_t CSTR_BUFSIZE = 1024;
char *c_str(const JSString *const &);
namespace basic {
template<lifetime L>
struct string
:JS::Rooted<JSString *>
:root<JSString *, L>
{
IRCD_OVERLOAD(literal)
using handle = JS::HandleString;
using handle_mutable = JS::MutableHandleString;
static constexpr const size_t CBUFS = 8;
static const size_t CBUFSZ;
const char *c_str() const; // Copy into rotating buf
char *c_str() const; // Copy into rotating buf
size_t native_size() const;
size_t size() const;
bool empty() const;
char16_t operator[](const size_t &at) const;
explicit operator std::string() const;
operator JS::Value() const;
char16_t operator[](const size_t &at) const;
using root<JSString *, L>::root;
string(literal_t, const char16_t *const &);
string(const char16_t *const &, const size_t &len);
string(const char16_t *const &);
@ -55,124 +58,135 @@ struct string
string(const char *const &, const size_t &len);
string(const std::string &);
string(const char *const &);
string(const value &);
string(const value<L> &);
string(JSString *const &);
string(JSString &);
string();
string(string &&) noexcept;
string(const string &) = delete;
friend int cmp(const string &, const string &);
friend int cmp(const string &, const char *const &);
friend int cmp(const char *const &, const string &);
friend int cmp(const string &, const std::string &);
friend int cmp(const std::string &, const string &);
struct less
{
using is_transparent = std::true_type;
template<class A, class B> bool operator()(const A &, const B &) const;
};
friend std::ostream &operator<<(std::ostream &, const string &);
};
string operator+(const string::handle &left, const string::handle &right);
string substr(const string::handle &, const size_t &pos, const size_t &len = -1);
std::pair<string, string> split(const string::handle &, const char16_t &);
std::pair<string, string> split(const string::handle &, const char &);
template<class T> constexpr bool is_string();
template<class A, class B> constexpr bool string_argument();
inline
string::string()
:JS::Rooted<JSString *>
template<lifetime A, lifetime B> int cmp(const string<A> &a, const string<B> &b);
template<lifetime L> int cmp(const char *const &a, const string<L> &b);
template<lifetime L> int cmp(const string<L> &a, const char *const &b);
template<lifetime L> int cmp(const string<L> &a, const std::string &b);
template<lifetime L> int cmp(const std::string &a, const string<L> &b);
template<lifetime L> bool operator==(const string<L> &a, const char *const &b);
template<lifetime L> bool operator==(const char *const &a, const string<L> &b);
template<class A,
class B>
using string_comparison = typename std::enable_if<string_argument<A, B>(), bool>::type;
template<class A, class B> string_comparison<A, B> operator==(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator!=(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator>(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator<(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator>=(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator<=(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator==(const A &a, const B &b);
template<class A, class B> string_comparison<A, B> operator!=(const A &a, const B &b);
template<lifetime L>
using string_pair = std::pair<string<L>, string<L>>;
template<lifetime L> string_pair<L> split(const typename string<L>::handle &s, const char &c);
template<lifetime L> string_pair<L> split(const typename string<L>::handle &s, const char16_t &c);
template<lifetime L> string<L> substr(const typename string<L>::handle &s, const size_t &pos, const size_t &len);
template<lifetime L> string<L> operator+(const typename string<L>::handle &left, const typename string<L>::handle &right);
template<lifetime L> std::ostream & operator<<(std::ostream &os, const string<L> &s);
} // namespace basic
using string = basic::string<lifetime::stack>;
using heap_string = basic::string<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
string<L>::string()
:string<L>::root::type
{
*cx,
JS_GetEmptyString(*rt)
}
{
}
inline
string::string(string &&other)
noexcept
:JS::Rooted<JSString *>{*cx, other}
template<lifetime L>
string<L>::string(JSString &val)
:string<L>::root::type{&val}
{
}
inline
string::string(JSString &val)
:JS::Rooted<JSString *>
template<lifetime L>
string<L>::string(JSString *const &val)
:string<L>::root::type
{
*cx, &val
}
{
}
inline
string::string(JSString *const &val)
:JS::Rooted<JSString *>
{
*cx,
likely(val)? val : throw internal_error("NULL string")
}
{
}
string::string(const value &val)
:JS::Rooted<JSString *>
template<lifetime L>
string<L>::string(const value<L> &val)
:string<L>::root::type
{
*cx,
JS::ToString(*cx, val)?: throw type_error("Failed to convert value to string")
}
{
}
inline
string::string(const std::string &s)
template<lifetime L>
string<L>::string(const std::string &s)
:string(s.data(), s.size())
{
}
inline
string::string(const char *const &s)
template<lifetime L>
string<L>::string(const char *const &s)
:string(s, strlen(s))
{
}
inline
string::string(const char *const &s,
template<lifetime L>
string<L>::string(const char *const &s,
const size_t &len)
:JS::Rooted<JSString *>
{
*cx, [&s, &len]
:string<L>::root::type{[&s, &len]
{
auto buf(native_external_copy(s, len));
return JS_NewExternalString(*cx, buf.release(), len, &native_external_delete);
}()
}
}()}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw type_error("Failed to construct string from character array");
}
inline
string::string(const std::u16string &s)
template<lifetime L>
string<L>::string(const std::u16string &s)
:string(s.data(), s.size())
{
}
inline
string::string(const char16_t *const &s)
template<lifetime L>
string<L>::string(const char16_t *const &s)
:string(s, std::char_traits<char16_t>::length(s))
{
}
inline
string::string(const char16_t *const &s,
template<lifetime L>
string<L>::string(const char16_t *const &s,
const size_t &len)
:JS::Rooted<JSString *>
{
*cx, [&s, &len]
:string<L>::root::type{[&s, &len]
{
// JS_NewExternalString does not require a null terminated buffer, but we are going
// to terminate anyway in case the deleter ever wants to iterate a canonical vector.
@ -180,71 +194,91 @@ string::string(const char16_t *const &s,
memcpy(buf.get(), s, len * 2);
buf.get()[len] = char16_t(0);
return JS_NewExternalString(*cx, buf.release(), len, &native_external_delete);
}()
}
}()}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw type_error("Failed to construct string from character array");
}
inline
string::string(literal_t,
template<lifetime L>
string<L>::string(literal_t,
const char16_t *const &s)
:JS::Rooted<JSString *>
:string<L>::root::type
{
*cx,
JS_NewExternalString(*cx, s, std::char_traits<char16_t>::length(s), &native_external_static)
}
{
if(unlikely(!get()))
if(unlikely(!this->get()))
throw type_error("Failed to construct string from wide character literal");
}
inline
template<lifetime L>
char16_t
string::operator[](const size_t &pos)
string<L>::operator[](const size_t &pos)
const
{
return at(get(), pos);
return at(this->get(), pos);
}
inline
string::operator JS::Value()
template<lifetime L>
string<L>::operator JS::Value()
const
{
return JS::StringValue(get());
return JS::StringValue(this->get());
}
inline
string::operator std::string()
template<lifetime L>
string<L>::operator std::string()
const
{
return native(get());
return native(this->get());
}
inline bool
string::empty()
template<lifetime L>
char *
string<L>::c_str()
const
{
return js::c_str(this->get());
}
template<lifetime L>
bool
string<L>::empty()
const
{
return size() == 0;
}
inline size_t
string::size()
template<lifetime L>
size_t
string<L>::size()
const
{
return js::size(get());
return js::size(this->get());
}
inline size_t
string::native_size()
template<lifetime L>
size_t
string<L>::native_size()
const
{
return js::native_size(get());
return js::native_size(this->get());
}
inline
std::ostream &operator<<(std::ostream &os, const string &s)
template<lifetime L>
template<class A,
class B>
bool
string<L>::less::operator()(const A &a, const B &b)
const
{
return cmp(a, b) < 0;
}
template<lifetime L>
std::ostream &
operator<<(std::ostream &os, const string<L> &s)
{
os << std::string(s);
return os;
@ -252,24 +286,7 @@ std::ostream &operator<<(std::ostream &os, const string &s)
template<class A,
class B>
constexpr bool
string_comparison()
{
return std::is_base_of<string, A>() || std::is_base_of<string, B>();
}
template<class A,
class B>
bool
string::less::operator()(const A &a, const B &b)
const
{
return cmp(a, b) < 0;
}
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
string_comparison<A, B>
operator>(const A &a, const B &b)
{
return cmp(a, b) > 0;
@ -277,7 +294,7 @@ operator>(const A &a, const B &b)
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
string_comparison<A, B>
operator<(const A &a, const B &b)
{
return cmp(a, b) < 0;
@ -285,7 +302,7 @@ operator<(const A &a, const B &b)
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
string_comparison<A, B>
operator>=(const A &a, const B &b)
{
return cmp(a, b) >= 0;
@ -293,14 +310,31 @@ operator>=(const A &a, const B &b)
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
string_comparison<A, B>
operator<=(const A &a, const B &b)
{
return cmp(a, b) <= 0;
}
inline bool
operator==(const string &a, const char *const &b)
template<class A,
class B>
string_comparison<A, B>
operator==(const A &a, const B &b)
{
return cmp(a, b) == 0;
}
template<class A,
class B>
string_comparison<A, B>
operator!=(const A &a, const B &b)
{
return !(operator==(a, b));
}
template<lifetime L>
bool
operator==(const string<L> &a, const char *const &b)
{
bool ret;
if(unlikely(!JS_StringEqualsAscii(*cx, a, b, &ret)))
@ -309,8 +343,9 @@ operator==(const string &a, const char *const &b)
return ret;
}
inline bool
operator==(const char *const &a, const string &b)
template<lifetime L>
bool
operator==(const char *const &a, const string<L> &b)
{
bool ret;
if(unlikely(!JS_StringEqualsAscii(*cx, b, a, &ret)))
@ -319,48 +354,43 @@ operator==(const char *const &a, const string &b)
return ret;
}
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
operator==(const A &a, const B &b)
{
return cmp(a, b) == 0;
}
template<class A,
class B>
typename std::enable_if<string_comparison<A, B>(), bool>::type
operator!=(const A &a, const B &b)
{
return !(operator==(a, b));
}
inline int
cmp(const string &a, const std::string &b)
template<lifetime L>
int
cmp(const string<L> &a,
const std::string &b)
{
return cmp(a, b.c_str());
}
inline int
cmp(const std::string &a, const string &b)
template<lifetime L>
int
cmp(const std::string &a,
const string<L> &b)
{
return cmp(a.c_str(), b);
}
inline int
cmp(const string &a, const char *const &b)
template<lifetime L>
int
cmp(const string<L> &a,
const char *const &b)
{
return cmp(a, string(b));
return cmp(a, string<L>(b));
}
inline int
cmp(const char *const &a, const string &b)
template<lifetime L>
int
cmp(const char *const &a,
const string<L> &b)
{
return cmp(string(a), b);
return cmp(string<L>(a), b);
}
inline int
cmp(const string &a, const string &b)
template<lifetime A,
lifetime B>
int
cmp(const string<A> &a,
const string<B> &b)
{
int32_t ret;
if(unlikely(!JS_CompareStrings(*cx, a, b, &ret)))
@ -369,15 +399,17 @@ cmp(const string &a, const string &b)
return ret;
}
inline std::pair<string, string>
split(const string::handle &s,
template<lifetime L>
std::pair<string<L>, string<L>>
split(const typename string<L>::handle &s,
const char &c)
{
return {};
}
inline std::pair<string, string>
split(const string::handle &s,
template<lifetime L>
std::pair<string<L>, string<L>>
split(const typename string<L>::handle &s,
const char16_t &c)
{
size_t i(0);
@ -385,12 +417,13 @@ split(const string::handle &s,
return
{
substr(s, 0, i),
i < size(s)? substr(s, i + 1, size(s) - i) : string()
i < size(s)? substr(s, i + 1, size(s) - i) : string<L>()
};
}
inline string
substr(const string::handle &s,
template<lifetime L>
string<L>
substr(const typename string<L>::handle &s,
const size_t &pos,
const size_t &len)
{
@ -402,13 +435,32 @@ substr(const string::handle &s,
return ret;
}
inline string
operator+(const string::handle &left,
const string::handle &right)
template<lifetime L>
string<L>
operator+(const typename string<L>::handle &left,
const typename string<L>::handle &right)
{
return JS_ConcatStrings(*cx, left, right);
}
template<class A,
class B>
constexpr bool
string_argument()
{
return is_string<A>() || is_string<B>();
}
template<class T>
constexpr bool
is_string()
{
return std::is_base_of<string<lifetime::stack>, T>() ||
std::is_base_of<string<lifetime::heap>, T>();
}
} // namespace basic
inline char16_t
at(const JSString *const &s,
const size_t &pos)

View file

@ -36,7 +36,7 @@ class trap
JSPropertySpec ps[2];
JSFunctionSpec fs[2];
std::unique_ptr<JSClass> _class;
std::map<string, trap *, string::less> children;
std::map<heap_string, trap *, heap_string::less> children;
// Override these to define JS objects in C
virtual value on_call(object::handle, const args &);

View file

@ -30,12 +30,12 @@ template<class T> T *pointer_value(const JS::Value &);
JS::Value pointer_value(const void *const &);
JS::Value pointer_value(void *const &);
struct value
:JS::Rooted<JS::Value>
{
using handle = JS::HandleValue;
using handle_mutable = JS::MutableHandleValue;
namespace basic {
template<lifetime L>
struct value
:root<JS::Value, L>
{
explicit operator std::string() const;
explicit operator double() const;
explicit operator uint64_t() const;
@ -45,14 +45,16 @@ struct value
explicit operator uint16_t() const;
explicit operator bool() const;
explicit value(const std::string &);
template<class T, lifetime LL> value(const root<T, LL> &r);
template<class T> value(const JS::MutableHandle<T> &h);
template<class T> value(const JS::Handle<T> &h);
value(const std::string &);
value(const char *const &);
value(const nullptr_t &);
value(const double &);
value(const float &);
value(const int32_t &);
value(const bool &);
value(const jsid &);
value(JSObject &);
value(JSObject *const &);
@ -60,189 +62,164 @@ struct value
value(JSFunction *const &);
value(JS::Symbol *const &);
value(const JS::Value &);
template<class T> value(const JS::Handle<T> &h);
template<class T> value(const JS::MutableHandle<T> &h);
template<class T> value(const JS::Rooted<T> &r);
template<class T> value(const JS::PersistentRooted<T> &p);
value();
value(value &&) noexcept;
value(const value &) = delete;
value &operator=(value &&) noexcept;
friend JSType type(const value &);
friend bool undefined(const value &);
};
inline
value::value()
:JS::Rooted<JS::Value>{*cx, JS::UndefinedValue()}
template<lifetime L> JSType type(const value<L> &);
template<lifetime L> bool undefined(const value<L> &);
} // namespace basic
using value = basic::value<lifetime::stack>;
using heap_value = basic::value<lifetime::heap>;
//
// Implementation
//
namespace basic {
template<lifetime L>
value<L>::value()
:value<L>::root::type{JS::UndefinedValue()}
{
}
inline
value::value(value &&other)
noexcept
:JS::Rooted<JS::Value>{*cx, other.get()}
{
other.set(JS::UndefinedValue());
}
inline value &
value::operator=(value &&other)
noexcept
{
set(other.get());
other.set(JS::UndefinedValue());
return *this;
}
template<class T>
value::value(const JS::PersistentRooted<T> &r)
:value(JS::Handle<T>(r))
template<lifetime L>
value<L>::value(const JS::Value &val)
:value<L>::root::type{val}
{
}
template<class T>
value::value(const JS::Rooted<T> &r)
:value(JS::Handle<T>(r))
template<lifetime L>
value<L>::value(JS::Symbol *const &val)
:value<L>::root::type{JS::SymbolValue(val)}
{
}
template<class T>
value::value(const JS::MutableHandle<T> &h)
:value(h.get())
template<lifetime L>
value<L>::value(JSObject *const &val)
:value<L>::root::type
{
}
template<class T>
value::value(const JS::Handle<T> &h)
:value(h.get())
{
}
inline
value::value(const JS::Value &val)
:JS::Rooted<JS::Value>{*cx, val}
{
}
inline
value::value(JS::Symbol *const &val)
:JS::Rooted<JS::Value>{*cx, JS::SymbolValue(val)}
{
}
inline
value::value(JSObject *const &val)
:JS::Rooted<JS::Value>
{
*cx,
val? JS::ObjectValue(*val) : throw internal_error("NULL JSObject")
}
{
}
inline
value::value(JSObject &val)
:JS::Rooted<JS::Value>{*cx, JS::ObjectValue(val)}
template<lifetime L>
value<L>::value(JSObject &val)
:value<L>::root::type{JS::ObjectValue(val)}
{
}
inline
value::value(JSString *const &val)
:JS::Rooted<JS::Value>{*cx, JS::StringValue(val)}
template<lifetime L>
value<L>::value(JSString *const &val)
:value<L>::root::type{JS::StringValue(val)}
{
}
inline
value::value(JSFunction *const &val)
:JS::Rooted<JS::Value>{*cx}
template<lifetime L>
value<L>::value(JSFunction *const &val)
:value<L>::root::type{}
{
auto *const obj(JS_GetFunctionObject(val));
if(unlikely(!obj))
throw type_error("Function cannot convert to Object");
set(JS::ObjectValue(*obj));
this->set(JS::ObjectValue(*obj));
}
inline
value::value(const jsid &val)
:JS::Rooted<JS::Value>{*cx}
template<lifetime L>
value<L>::value(const jsid &val)
:value<L>::root::type{}
{
if(!JS_IdToValue(*cx, val, &(*this)))
throw type_error("Failed to construct value from Id");
}
inline
value::value(const bool &b)
:JS::Rooted<JS::Value>{*cx, JS::BooleanValue(b)}
template<lifetime L>
value<L>::value(const bool &b)
:value<L>::root::type{JS::BooleanValue(b)}
{
}
inline
value::value(const int32_t &val)
:JS::Rooted<JS::Value>{*cx, JS::Int32Value(val)}
template<lifetime L>
value<L>::value(const int32_t &val)
:value<L>::root::type{JS::Int32Value(val)}
{
}
inline
value::value(const float &val)
:JS::Rooted<JS::Value>{*cx, JS::Float32Value(val)}
template<lifetime L>
value<L>::value(const float &val)
:value<L>::root::type{JS::Float32Value(val)}
{
}
inline
value::value(const double &val)
:JS::Rooted<JS::Value>{*cx, JS::DoubleValue(val)}
template<lifetime L>
value<L>::value(const double &val)
:value<L>::root::type{JS::DoubleValue(val)}
{
}
inline
value::value(const nullptr_t &)
:JS::Rooted<JS::Value>{*cx, JS::NullValue()}
template<lifetime L>
value<L>::value(const nullptr_t &)
:value<L>::root::type{JS::NullValue()}
{
}
inline
value::value(const std::string &s)
:JS::Rooted<JS::Value>
{
*cx, [&s]
template<lifetime L>
value<L>::value(const std::string &s)
:value<L>::root::type{[&s]
{
auto buf(native_external_copy(s));
const auto ret(JS_NewExternalString(*cx, buf.release(), s.size(), &native_external_delete));
return JS::StringValue(ret);
}()
}
}()}
{
}
inline
value::value(const char *const &s)
:JS::Rooted<JS::Value>
{
*cx, !s? JS::NullValue() : [&s]
template<lifetime L>
value<L>::value(const char *const &s)
:value<L>::root::type{!s? JS::NullValue() : [&s]
{
const auto len(strlen(s));
auto buf(native_external_copy(s, len));
const auto ret(JS_NewExternalString(*cx, buf.release(), len, &native_external_delete));
return JS::StringValue(ret);
}()
}
}()}
{
}
inline
value::operator bool()
template<lifetime L>
template<class T>
value<L>::value(const JS::Handle<T> &h)
:value(h.get())
{
}
template<lifetime L>
template<class T>
value<L>::value(const JS::MutableHandle<T> &h)
:value(h.get())
{
}
template<lifetime L>
template<class T,
lifetime LL>
value<L>::value(const root<T, LL> &r):
value(r.get())
{
}
template<lifetime L>
value<L>::operator bool()
const
{
return JS::ToBoolean(*this);
}
inline
value::operator uint16_t()
template<lifetime L>
value<L>::operator uint16_t()
const
{
uint16_t ret;
@ -252,8 +229,8 @@ const
return ret;
}
inline
value::operator int32_t()
template<lifetime L>
value<L>::operator int32_t()
const
{
int32_t ret;
@ -263,8 +240,8 @@ const
return ret;
}
inline
value::operator uint32_t()
template<lifetime L>
value<L>::operator uint32_t()
const
{
uint32_t ret;
@ -274,8 +251,8 @@ const
return ret;
}
inline
value::operator int64_t()
template<lifetime L>
value<L>::operator int64_t()
const
{
int64_t ret;
@ -285,8 +262,8 @@ const
return ret;
}
inline
value::operator uint64_t()
template<lifetime L>
value<L>::operator uint64_t()
const
{
uint64_t ret;
@ -296,8 +273,8 @@ const
return ret;
}
inline
value::operator double()
template<lifetime L>
value<L>::operator double()
const
{
double ret;
@ -307,26 +284,30 @@ const
return ret;
}
inline
value::operator std::string()
template<lifetime L>
value<L>::operator std::string()
const
{
const auto s(JS::ToString(*cx, *this));
return s? native(s) : throw type_error("Failed to cast to string");
}
inline JSType
type(const value &val)
template<lifetime L>
JSType
type(const value<L> &val)
{
return JS_TypeOfValue(*cx, val);
}
inline bool
undefined(const value &val)
template<lifetime L>
bool
undefined(const value<L> &val)
{
return type(val) == JSTYPE_VOID;
}
} // namespace basic
inline JS::Value
pointer_value(const void *const &ptr)
{

View file

@ -852,36 +852,6 @@ ircd::js::trap::on_call(object::handle,
// ircd/js/script.h
//
ircd::js::script::script(const JS::CompileOptions &opts,
const std::string &src)
:JS::Rooted<JSScript *>{*cx}
{
if(!JS::Compile(*cx, opts, src.data(), src.size(), &(*this)))
throw syntax_error("Failed to compile script");
}
ircd::js::value
ircd::js::script::operator()()
const
{
value ret;
if(!JS_ExecuteScript(*cx, *this, &ret))
throw jserror(jserror::pending);
return ret;
}
ircd::js::value
ircd::js::script::operator()(JS::AutoObjectVector &stack)
const
{
value ret;
if(!JS_ExecuteScript(*cx, stack, *this, &ret))
throw jserror(jserror::pending);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/function_literal.h
@ -890,7 +860,7 @@ const
ircd::js::function_literal::function_literal(const char *const &name,
const std::initializer_list<const char *> &prototype,
const char *const &text)
:JS::PersistentRooted<JSFunction *>{*cx}
:root<JSFunction *, lifetime::persist>{}
,name{name}
,text{text}
,prototype{prototype}
@ -913,10 +883,9 @@ ircd::js::function_literal::function_literal(const char *const &name,
ircd::js::function_literal::function_literal(function_literal &&other)
noexcept
:JS::PersistentRooted<JSFunction *>
:root<JSFunction *, lifetime::persist>
{
*cx,
other
std::move(other)
}
,name{std::move(other.name)}
,text{std::move(other.text)}
@ -929,53 +898,6 @@ noexcept
// ircd/js/function.h
//
ircd::js::function::function(JS::AutoObjectVector &stack,
const JS::CompileOptions &opts,
const char *const &name,
const std::vector<std::string> &args,
const std::string &src)
:JS::Rooted<JSFunction *>{*cx}
{
std::vector<const char *> argp(args.size());
std::transform(begin(args), end(args), begin(argp), []
(const std::string &arg)
{
return arg.data();
});
if(!JS::CompileFunction(*cx,
stack,
opts,
name,
argp.size(),
&argp.front(),
src.data(),
src.size(),
&(*this)))
{
throw syntax_error("Failed to compile function");
}
}
ircd::js::value
ircd::js::function::operator()(const object &that)
const
{
return operator()(that, JS::HandleValueArray::empty());
}
ircd::js::value
ircd::js::function::operator()(const object &that,
const JS::HandleValueArray &args)
const
{
value ret;
if(!JS_CallFunction(*cx, that, *this, args, &ret))
throw jserror(jserror::pending);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/call.h
@ -1206,21 +1128,15 @@ ircd::js::has(const object::handle &obj,
// ircd/js/string.h
//
const size_t ircd::js::string::CBUFSZ
char *
ircd::js::c_str(const JSString *const &str)
{
1024
};
const char *
ircd::js::string::c_str()
const
{
static thread_local char cbuf[CBUFS][CBUFSZ];
static thread_local char cbuf[CSTR_BUFS][CSTR_BUFSIZE];
static thread_local size_t ctr;
char *const buf(cbuf[ctr]);
native(get(), buf, CBUFSZ);
ctr = (ctr + 1) % CBUFS;
native(str, buf, CSTR_BUFSIZE);
ctr = (ctr + 1) % CSTR_BUFS;
return buf;
}