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:
parent
7e66df914a
commit
6b5bab0871
11 changed files with 906 additions and 636 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
267
include/ircd/js/root.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
100
ircd/js.cc
100
ircd/js.cc
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue