0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-02 18:18:56 +02:00

ircd::js: Add js exception hierarchy.

This commit is contained in:
Jason Volk 2016-10-17 12:16:30 -07:00
parent 2c231ac187
commit 4171a9c834
3 changed files with 208 additions and 3 deletions

84
include/ircd/js/error.h Normal file
View file

@ -0,0 +1,84 @@
/*
* 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_ERROR_H
namespace ircd {
namespace js {
class error_handler
{
friend class runtime;
using closure = std::function<void (const char *const &msg, JSErrorReport &)>;
error_handler *theirs;
closure handler;
public:
error_handler(const closure &handler);
error_handler(closure &&handler);
~error_handler() noexcept;
};
struct jserror
:js::error
{
protected:
std::u16string msg;
JSErrorReport report;
void generate(const JSExnType &type, const char *const &fmt, va_list ap);
public:
JS::Value create_error(const JS::HandleObject &stack, const JS::HandleString &file, const std::pair<uint, uint> &linecol) const;
JS::Value create_error() const;
void set_pending() const;
jserror(generate_skip_t);
jserror(const char *fmt = " ", ...) AFP(2, 3);
};
#define IRCD_JS_ERROR_DEF(name, type) \
struct name \
:jserror \
{ \
name(const char *const fmt = " ", ...) AFP(2, 3) \
:jserror(generate_skip) \
{ \
va_list ap; \
va_start(ap, fmt); \
generate(type, fmt, ap); \
va_end(ap); \
} \
};
IRCD_JS_ERROR_DEF( internal_error, JSEXN_INTERNALERR )
IRCD_JS_ERROR_DEF( eval_error, JSEXN_EVALERR )
IRCD_JS_ERROR_DEF( range_error, JSEXN_RANGEERR )
IRCD_JS_ERROR_DEF( reference_error, JSEXN_REFERENCEERR )
IRCD_JS_ERROR_DEF( syntax_error, JSEXN_SYNTAXERR )
IRCD_JS_ERROR_DEF( type_error, JSEXN_TYPEERR )
IRCD_JS_ERROR_DEF( uri_error, JSEXN_URIERR )
} // namespace js
} // namespace ircd

View file

@ -62,6 +62,7 @@ JSVersion version(const char *const &v) { return JS_StringToVersion(v);
#include "runtime.h"
#include "context.h"
#include "compartment.h"
#include "error.h"
#include "id.h"
#include "string.h"
#include "for_each.h"

View file

@ -321,6 +321,29 @@ const
va_end(ap);
}
void
ircd::js::trap::host_exception(const char *const fmt,
...)
const
{
va_list ap;
va_start(ap, fmt);
char buf[1024];
vsnprintf(buf, sizeof(buf), fmt, ap);
log.error("trap(%p) \"%s\": %s",
reinterpret_cast<const void *>(this),
name().c_str(),
buf);
JS_ReportError(*cx, "BUG: trap(%p) \"%s\" %s",
reinterpret_cast<const void *>(this),
name().c_str(),
buf);
va_end(ap);
}
bool
ircd::js::trap::on_ctor(const unsigned &argc,
JS::Value &argv)
@ -417,6 +440,92 @@ ircd::js::string_convert(const char16_t *const &s)
return s? converter.to_bytes(s) : std::string{};
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/error.h
//
ircd::js::error_handler::error_handler(closure &&handler)
:theirs{rt->error_handler}
,handler{std::move(handler)}
{
rt->error_handler = this;
}
ircd::js::error_handler::error_handler(const closure &handler)
:theirs{rt->error_handler}
,handler{handler}
{
rt->error_handler = this;
}
ircd::js::error_handler::~error_handler()
noexcept
{
assert(rt->error_handler == this);
rt->error_handler = theirs;
}
ircd::js::jserror::jserror(generate_skip_t)
:ircd::js::error(generate_skip)
{
}
ircd::js::jserror::jserror(const char *const fmt,
...)
:ircd::js::error(generate_skip)
{
va_list ap;
va_start(ap, fmt);
generate(JSEXN_ERR, fmt, ap);
va_end(ap);
}
void
ircd::js::jserror::set_pending()
const
{
JS::RootedValue ex(*cx, create_error());
JS_SetPendingException(*cx, ex);
}
JS::Value
ircd::js::jserror::create_error(const JS::HandleObject &stack,
const JS::HandleString &file,
const std::pair<uint, uint> &linecol)
const
{
JS::RootedValue ret(*cx);
JS::RootedString msg(*cx);
const auto type((JSExnType)report.exnType);
const auto &line(linecol.first);
const auto &col(linecol.second);
if(!JS::CreateError(*cx, type, stack, file, line, col, const_cast<JSErrorReport *>(&report), msg, &ret))
throw error("Failed to construct jserror exception!");
return ret;
}
JS::Value
ircd::js::jserror::create_error()
const
{
JS::RootedObject stack(*cx);
JS::RootedString file(*cx);
return create_error(stack, file, {0, 0});
}
void
ircd::js::jserror::generate(const JSExnType &type,
const char *const &fmt,
va_list ap)
{
ircd::exception::generate(fmt, ap);
msg = string_convert(what());
report.ucmessage = msg.c_str();
report.exnType = type;
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/debug.h
@ -731,7 +840,8 @@ ircd::js::runtime::runtime(const struct opts &opts)
JS_NewRuntime(opts.maxbytes),
JS_DestroyRuntime
}
,opts(opts)
,error_handler{nullptr}
,opts{opts}
{
// We use their privdata to find `this` via our(JSRuntime*) function.
// Any additional user privdata will have to ride a member in this class itself.
@ -756,7 +866,8 @@ ircd::js::runtime::runtime(const struct opts &opts)
ircd::js::runtime::runtime(runtime &&other)
noexcept
:custom_ptr<JSRuntime>{std::move(other)}
,opts(std::move(other.opts))
,error_handler{nullptr}
,opts{std::move(other.opts)}
{
// Branch not taken for null/defaulted instance of JSRuntime smart ptr
if(!!*this)
@ -771,6 +882,7 @@ ircd::js::runtime::operator=(runtime &&other)
noexcept
{
static_cast<custom_ptr<JSRuntime> &>(*this) = std::move(other);
error_handler = std::move(other.error_handler);
opts = std::move(other.opts);
// Branch not taken for null/defaulted instance of JSRuntime smart ptr
@ -860,6 +972,14 @@ void
ircd::js::runtime::handle_error(JSContext *const ctx,
const char *const msg,
JSErrorReport *const report)
noexcept
{
log.error("JSContext(%p): %s [%s]", (const void *)ctx, msg, debug(*report).c_str());
if(!rt->error_handler)
{
log.error("Unhandled: JSContext(%p): %s [%s]", (const void *)ctx, msg, debug(*report).c_str());
return;
}
assert(report);
rt->error_handler->handler(msg, *report);
}