0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-25 23:14:13 +01:00

ircd::js: Add trap::property; Improve trap construction interface.

This commit is contained in:
Jason Volk 2016-11-24 19:27:47 -08:00
parent 8d92c8a9c4
commit 01d814ea1a
6 changed files with 465 additions and 72 deletions

View file

@ -36,8 +36,7 @@ inline object
ctor(trap &trap,
const vector<value>::handle &args = {})
{
object proto(trap());
return JS_New(*cx, proto, args);
return trap.construct(args);
}
} // namespace js

View file

@ -91,6 +91,7 @@ inline JSVersion version(const char *const &v) { return JS_StringToVersion(v);
#include "args.h"
#include "trap.h"
#include "trap_function.h"
#include "trap_property.h"
#include "ctor.h"
#include "generator.h"
#include "global.h"

View file

@ -27,23 +27,26 @@ namespace js {
struct trap
{
struct property;
struct function;
protected:
const std::string parent;
trap *parent;
const std::string _name; // don't touch
std::array<JSPropertySpec, 32> sps; // static property spec
std::array<JSPropertySpec, 32> ps; // property spec
std::array<JSFunctionSpec, 32> sfs; // static function spec
std::array<JSFunctionSpec, 32> fs; // function spec
std::unique_ptr<JSClass> _class;
std::map<std::string, property *> member;
std::map<std::string, function *> memfun;
std::map<std::string, trap *> children;
std::set<function *> memfun;
trap *parent_prototype;
trap *prototype;
std::array<JSConstIntegerSpec, 32> cis; // static const integer spec
std::array<JSConstDoubleSpec, 32> cds; // static const double spec
std::array<JSPropertySpec, 32> sps; // static property spec
std::array<JSFunctionSpec, 32> sfs; // static function spec
std::array<JSPropertySpec, 32> ps; // property spec
std::array<JSFunctionSpec, 32> fs; // function spec
std::unique_ptr<JSClass> _class; // class spec
trap *prototrap; // pointer to __proto__ trap
static trap &from(const JSObject &);
static trap &from(const JS::HandleObject &);
static trap &from(const JSObject *const &);
protected:
void debug(const void *const &that, const char *fmt, ...) const AFP(3, 4);
@ -74,6 +77,8 @@ struct trap
static bool handle_del(JSContext *, JS::HandleObject, JS::HandleId, JS::ObjectOpResult &) noexcept;
static bool handle_has(JSContext *, JS::HandleObject, JS::HandleId, bool *resolved) noexcept;
static bool handle_enu(JSContext *, JS::HandleObject) noexcept;
static bool handle_setter(JSContext *, unsigned argc, JS::Value *argv) noexcept;
static bool handle_getter(JSContext *, unsigned argc, JS::Value *argv) noexcept;
static bool handle_call(JSContext *, unsigned argc, JS::Value *argv) noexcept;
static bool handle_ctor(JSContext *, unsigned argc, JS::Value *argv) noexcept;
static void handle_dtor(JSFreeOp *, JSObject *) noexcept;
@ -81,6 +86,7 @@ struct trap
public:
auto &name() const { return _name; }
auto &jsclass() const { return *_class; }
auto &jsclass() { return *_class; }
// Get child by name (NOT PATH)
const trap &child(const std::string &name) const;
@ -93,10 +99,13 @@ struct trap
operator const JSClass &() const { return jsclass(); }
operator const JSClass *() const { return &jsclass(); }
// Produces prototype. Call ctor(trap) for full construction
object operator()();
object prototype(const object::handle &globals);
object construct(const object::handle &globals, const vector<value>::handle &argv = {});
object construct(const vector<value>::handle &argv = {});
template<class... args> object operator()(args&&...);
trap(const std::string &path, const uint &flags = 0, const uint &prop_flags = 0);
trap(trap &parent, const std::string &name, const uint &flags = 0, const uint &prop_flags = 0);
trap(const std::string &name, const uint &flags = 0, const uint &prop_flags = 0);
trap(trap &&) = delete;
trap(const trap &) = delete;
virtual ~trap() noexcept;
@ -106,3 +115,11 @@ extern __thread trap *tree;
} // namespace js
} // namespace ircd
template<class... args>
ircd::js::object
ircd::js::trap::operator()(args&&... a)
{
vector<value> argv{{std::forward<args>(a)...}};
return construct(argv);
}

View file

@ -43,7 +43,7 @@ struct trap::function
static bool handle_call(JSContext *, unsigned, JS::Value *) noexcept;
public:
js::function operator()(const object::handle &);
js::function operator()(const object::handle &) const;
function(trap &,
std::string name,

View file

@ -0,0 +1,50 @@
/*
* 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_TRAP_PROPERTY
namespace ircd {
namespace js {
struct trap::property
{
using function = js::function;
struct trap *trap;
const std::string name;
virtual value on_set(function::handle, object::handle, value::handle);
virtual value on_get(function::handle, object::handle);
private:
static bool handle_set(JSContext *c, unsigned argc, JS::Value *argv) noexcept;
static bool handle_get(JSContext *c, unsigned argc, JS::Value *argv) noexcept;
public:
property(struct trap &, std::string name);
property(property &&) = delete;
property(const property &) = delete;
virtual ~property() noexcept;
};
} // namespace js
} // namespace ircd

View file

@ -436,7 +436,14 @@ ircd::js::global::global(trap &trap,
if(!JS_InitStandardClasses(*cx, *this))
throw error("Failed to init standard classes for global object");
for(auto it(begin(trap.memfun)); it != end(trap.memfun); ++it)
{
trap::function &deffun(*it->second);
deffun(*this);
}
JS_InitReflectParse(*cx, *this);
JS_FireOnNewGlobalObject(*cx, *this);
}
@ -463,6 +470,169 @@ noexcept
// ircd/js/generator.h
//
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/trap_property.h
//
ircd::js::trap::property::property(struct trap &trap,
std::string name)
try
:trap{&trap}
,name{std::move(name)}
{
const auto it(std::find_if(begin(trap.ps), end(trap.ps), []
(const JSPropertySpec &ps)
{
return !ps.name;
}));
if(it == end(trap.ps))
throw error("out of slots");
{
const auto it(trap.member.emplace(this->name, this));
if(!it.second)
throw error("already exists");
}
JSPropertySpec &spec(*it);
spec.name = this->name.c_str();
spec.flags = JSPROP_SHARED;
spec.getter.native.op = handle_get;
spec.setter.native.op = handle_set;
log.debug("Registered property '%s' on trap '%s'",
this->name.c_str(),
trap.name().c_str());
}
catch(const error &e)
{
throw error("Failed to register property '%s': out slots on trap '%s': %s",
this->name.c_str(),
trap.name().c_str(),
e.what());
}
ircd::js::trap::property::~property()
noexcept
{
assert(trap);
const auto it(std::find_if(begin(trap->ps), end(trap->ps), [this]
(const JSPropertySpec &spec)
{
return name == spec.name;
}));
if(it != end(trap->ps))
{
JSPropertySpec &spec(*it);
memset(&spec, 0x0, sizeof(spec));
}
const size_t erased(trap->member.erase(name));
assert(erased);
}
bool
ircd::js::trap::property::handle_get(JSContext *const c,
const unsigned argc,
JS::Value *const argv)
noexcept try
{
using js::function;
const struct args args(argc, argv);
object that(args.computeThis(c));
function func(args.callee());
const string name(js::name(func));
auto &trap(from(that));
trap.debug(that.get(), "get '%s' (property)",
name.c_str());
property &prop(*trap.member.at(name));
args.rval().set(prop.on_get(func, that));
return true;
}
catch(const jserror &e)
{
e.set_pending();
return false;
}
catch(const std::exception &e)
{
auto ca(JS::CallArgsFromVp(argc, argv));
object that(ca.computeThis(c));
auto &trap(from(that));
trap.host_exception(that.get(), "property get: %s", e.what());
return false;
}
namespace ircd {
namespace js {
struct foodata
:priv_data
{
trap::property *ptr;
foodata(trap::property *const &ptr = nullptr): ptr{ptr} {}
};
} // namespace js
} // namespace ircd
bool
ircd::js::trap::property::handle_set(JSContext *const c,
const unsigned argc,
JS::Value *const argv)
noexcept try
{
using js::function;
const struct args args(argc, argv);
object that(args.computeThis(c));
function func(args.callee());
const string name(js::name(func));
auto &trap(from(that));
trap.debug(that.get(), "set '%s' (property)",
name.c_str());
property &prop(*trap.member.at(name));
args.rval().set(prop.on_get(func, that));
return true;
}
catch(const jserror &e)
{
e.set_pending();
return false;
}
catch(const std::exception &e)
{
auto ca(JS::CallArgsFromVp(argc, argv));
object that(ca.computeThis(c));
auto &trap(from(that));
trap.host_exception(that.get(), "property set: %s", e.what());
return false;
}
ircd::js::value
ircd::js::trap::property::on_get(function::handle,
object::handle that)
{
return {};
}
ircd::js::value
ircd::js::trap::property::on_set(function::handle,
object::handle that,
value::handle val)
{
return val;
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/trap_function.h
@ -479,17 +649,18 @@ ircd::js::trap::function::function(trap &member,
,flags{flags}
,lambda{lambda}
{
member.memfun.emplace(this);
member.memfun.emplace(this->name, this);
}
ircd::js::trap::function::~function()
noexcept
{
member->memfun.erase(this);
member->memfun.erase(this->name);
}
ircd::js::function
ircd::js::trap::function::operator()(const object::handle &obj)
const
{
const auto jsf(::js::DefineFunctionWithReserved(*cx,
obj,
@ -514,8 +685,8 @@ noexcept try
assert(&our(c) == cx);
const struct args args(argc, argv);
const value that(args.computeThis(c));
const object func(args.callee());
const value that(args.computeThis(c));
auto &trap(from(func));
log.debug("trap(%p) this(%p) %s() call argv[%u]",
(const void *)&trap,
@ -574,21 +745,24 @@ ircd::js::trap::function::on_call(object::handle obj,
// ircd/js/trap.h
//
ircd::js::trap::trap(const std::string &path,
ircd::js::trap::trap(const std::string &name,
const uint &flags,
const uint &prop_flags)
:parent{[&path]
:trap{*tree, name, flags, prop_flags}
{
const auto ret(rsplit(path, ".").first);
return ret == path? std::string{} : ret;
}()}
,_name
{
path.empty()? std::string{""} : token_last(path, ".")
}
ircd::js::trap::trap(trap &parent,
const std::string &name,
const uint &flags,
const uint &prop_flags)
:parent{&parent != this? &parent : nullptr}
,_name{name}
,cis{0}
,cds{0}
,sps{0}
,ps{0}
,sfs{0}
,ps{0}
,fs{0}
,_class{std::make_unique<JSClass>(JSClass
{
@ -608,13 +782,14 @@ ircd::js::trap::trap(const std::string &path,
flags & JSCLASS_GLOBAL_FLAGS? JS_GlobalObjectTraceHook : handle_trace,
{ this } // reserved[0] TODO: ?????????
})}
,parent_prototype{nullptr}
,prototype{nullptr}
,prototrap{nullptr}
{
std::fill(begin(sfs), end(sfs), (JSFunctionSpec)JS_FS_END);
std::fill(begin(fs), end(fs), (JSFunctionSpec)JS_FS_END);
ps[0].flags |= prop_flags;
//ps[0].name = "";
//ps[0].flags |= prop_flags;
//ps[0].flags |= JSPROP_ENUMERATE;
add_this();
}
@ -633,27 +808,51 @@ noexcept
}
ircd::js::object
ircd::js::trap::operator()()
ircd::js::trap::construct(const vector<value>::handle &argv)
{
object p(prototype? ctor(*prototype) : object{});
object pp(parent_prototype? ctor(*parent_prototype) : object{});
object proto(JS_InitClass(*cx,
p,
pp,
_class.get(),
nullptr,
0,
ps.data(),
fs.data(),
sps.data(),
sfs.data()));
const object globals;
return construct(globals, argv);
}
ircd::js::object
ircd::js::trap::construct(const object::handle &globals,
const vector<value>::handle &argv)
{
const object prototype(this->prototype(globals));
return JS_New(*cx, prototype, argv);
}
ircd::js::object
ircd::js::trap::prototype(const object::handle &globals)
{
const object super
{
prototrap? prototrap->construct() : object{object::uninitialized}
};
const object proto
{
JS_InitClass(*cx,
globals,
super,
_class.get(),
nullptr,
0,
ps.data(),
fs.data(),
sps.data(),
sfs.data())
};
for(auto it(begin(memfun)); it != end(memfun); ++it)
{
trap::function &deffun(**it);
deffun(proto);
const function &deffun(*it->second);
const js::function func(deffun(proto));
}
JS_DefineConstIntegers(*cx, proto, cis.data());
JS_DefineConstDoubles(*cx, proto, cds.data());
return proto;
}
@ -661,25 +860,28 @@ void
ircd::js::trap::del_this()
try
{
if(name().empty())
// It is a special condition when the parent is self (this) and the trap has no name.
if(!parent && name().empty())
{
tree = nullptr;
tree = nullptr; // thread_local
return;
}
auto &parent(find(this->parent));
if(!parent.children.erase(name()))
if(!parent)
return;
if(!parent->children.erase(name()))
throw std::out_of_range("child not in parent's map");
log.debug("Unregistered trap '%s' in `%s'",
name().c_str(),
this->parent.c_str());
parent->name().c_str());
}
catch(const std::exception &e)
{
log.error("Failed to unregister object trap '%s' in `%s': %s",
name().c_str(),
parent.c_str(),
parent->name().c_str(),
e.what());
return;
}
@ -688,36 +890,29 @@ void
ircd::js::trap::add_this()
try
{
if(name().empty())
// It is a special condition when the parent is self (this) and the trap has no name.
if(!parent && name().empty())
{
if(tree)
throw error("ircd::js::tree is already active. Won't overwrite.");
tree = this;
tree = this; // thread_local
return;
}
auto &parent(find(this->parent));
const auto iit(parent.children.emplace(name(), this));
if(!parent)
return;
const auto iit(parent->children.emplace(name(), this));
if(!iit.second)
throw error("Failed to overwrite existing");
log.debug("Registered trap '%s' in `%s'",
name().c_str(),
this->parent.c_str());
}
catch(const std::out_of_range &e)
{
log.error("Failed to register object trap '%s' in `%s': missing parent.",
name().c_str(),
parent.c_str());
throw;
parent->name().c_str());
}
catch(const std::exception &e)
{
log.error("Failed to register object trap '%s' in `%s': %s",
name().c_str(),
parent.c_str(),
parent->name().c_str(),
e.what());
throw;
}
@ -953,12 +1148,118 @@ catch(const jserror &e)
catch(const std::exception &e)
{
auto &trap(from(obj));
trap.host_exception("del '%s': %s",
trap.host_exception(obj.get(), "del '%s': %s",
string(id).c_str(),
e.what());
return false;
}
namespace ircd {
namespace js {
std::map<std::string, heap_value> tempo;
}
}
bool
ircd::js::trap::handle_getter(JSContext *const c,
unsigned argc,
JS::Value *const argv)
noexcept try
{
using js::function;
const struct args args(argc, argv);
object that(args.computeThis(c));
function func(args.callee());
const string name(js::name(func));
auto &trap(from(that));
trap.debug(that.get(), "get '%s' (getter)",
name.c_str());
const auto it(tempo.find(name));
if(it == end(tempo))
{
//throw reference_error("%s", name.c_str());
args.rval().set(value{});
return true;
}
heap_value &val(it->second);
args.rval().set(val);
return true;
}
catch(const jserror &e)
{
e.set_pending();
return false;
}
catch(const std::exception &e)
{
auto ca(JS::CallArgsFromVp(argc, argv));
object that(ca.computeThis(c));
auto &trap(from(that));
trap.host_exception(that.get(), "getter: %s", e.what());
return false;
}
bool
ircd::js::trap::handle_setter(JSContext *const c,
unsigned argc,
JS::Value *const argv)
noexcept try
{
using js::function;
const struct args args(argc, argv);
object that(args.computeThis(c));
function func(args.callee());
value val(args[0]);
const auto type(basic::type(val));
const string name(js::name(func));
auto &trap(from(that));
trap.debug(that.get(), "set '%s' (%s) (setter)",
name.c_str(),
reflect(type));
auto it(tempo.lower_bound(name));
assert(it != end(tempo));
heap_value &hval(it->second);
switch(js::type(type))
{
case jstype::OBJECT:
{
//const auto flags(JSPROP_SHARED);
//object ret(JS_DefineObject(*cx, object(val), "", &trap.jsclass(), flags));
//tempo.emplace(name, heap_value(ret));
//args.rval().set(ret);
//return true;
}
default:
hval = val;
args.rval().set(val);
return true;
}
}
catch(const jserror &e)
{
e.set_pending();
return false;
}
catch(const std::exception &e)
{
auto ca(JS::CallArgsFromVp(argc, argv));
object that(ca.computeThis(c));
auto &trap(from(that));
trap.host_exception(that.get(), "setter: %s", e.what());
return false;
}
bool
ircd::js::trap::handle_get(JSContext *const c,
JS::HandleObject obj,
@ -1108,9 +1409,9 @@ catch(const std::exception &e)
}
ircd::js::trap &
ircd::js::trap::from(const JS::HandleObject &o)
ircd::js::trap::from(const JSObject *const &o)
{
return from(*o.get());
return from(*o);
}
ircd::js::trap &
@ -1206,6 +1507,31 @@ bool
ircd::js::trap::on_has(object::handle,
id::handle id)
{
/*
const string sid(id);
if(children.count(sid))
return false;
if(std::any_of(begin(memfun), end(memfun), [&sid]
(const auto &it)
{
const auto &memfun(*it.second);
return sid == memfun.name;
}))
return false;
*/
/*
value val;
const auto flags(JSPROP_SHARED | JSPROP_ENUMERATE);
if(!JS_DefinePropertyById(*cx, obj, id, val, flags, handle_getter, handle_setter))
throw jserror("Failed to define property '%s'", sid.c_str());
*/
// const auto flags(0);
// if(!JS_DefineObject(*cx, obj, sid.c_str(), &jsclass(), flags))
// throw jserror("Failed to define property '%s'", sid.c_str());
return false;
}