2016-10-12 09:04:26 +02:00
|
|
|
/*
|
|
|
|
* 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_CONTEXT_H
|
|
|
|
|
|
|
|
namespace ircd {
|
|
|
|
namespace js {
|
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// Indicates the phase of execution of the javascript
|
|
|
|
enum class phase
|
|
|
|
:uint8_t
|
|
|
|
{
|
|
|
|
ACCEPT, // JS is not running.
|
|
|
|
ENTER, // JS is currently executing or is committed to being entered.
|
|
|
|
INTR, // An interrupt request has or is committed to being sent.
|
|
|
|
};
|
|
|
|
|
|
|
|
// Indicates what operation the interrupt is for
|
|
|
|
enum class irq
|
|
|
|
:uint8_t
|
|
|
|
{
|
|
|
|
NONE, // Sentinel value (no interrupt) (spurious)
|
|
|
|
JS, // JS itself triggers an interrupt after data init before code exec.
|
|
|
|
USER, // User interrupts to have handler (on_intr) called.
|
|
|
|
YIELD, // An ircd::ctx yield should take place, then javascript continues.
|
|
|
|
TERMINATE, // The javascript should be terminated.
|
|
|
|
};
|
|
|
|
|
2016-10-12 09:04:26 +02:00
|
|
|
struct context
|
2016-10-14 02:24:50 +02:00
|
|
|
:private custom_ptr<JSContext>
|
2016-10-12 09:04:26 +02:00
|
|
|
{
|
2016-10-22 05:32:45 +02:00
|
|
|
// Context options
|
|
|
|
struct opts
|
|
|
|
{
|
|
|
|
size_t stack_chunk_size = 8_KiB;
|
|
|
|
microseconds timer_limit = 10ms;
|
|
|
|
}
|
|
|
|
opts;
|
2016-10-16 01:53:44 +02:00
|
|
|
|
2016-10-22 05:32:45 +02:00
|
|
|
// Exception state
|
|
|
|
JSExceptionState *except; // Use save_exception()/restore_exception()
|
|
|
|
JSErrorReport report; // Note: ptrs may not be valid in here.
|
|
|
|
|
|
|
|
// Interruption state
|
2016-10-20 12:25:41 +02:00
|
|
|
struct alignas(8) state
|
|
|
|
{
|
|
|
|
uint32_t sem;
|
|
|
|
enum phase phase;
|
|
|
|
enum irq irq;
|
2016-10-16 01:53:44 +02:00
|
|
|
};
|
2016-10-20 12:25:41 +02:00
|
|
|
std::atomic<struct state> state; // Atomic state of execution
|
|
|
|
std::function<int (const irq &)> on_intr; // User interrupt hook (ret -1 to not interfere)
|
2016-10-22 05:32:45 +02:00
|
|
|
bool handle_interrupt(); // Called by runtime on interrupt
|
2016-10-20 04:50:55 +02:00
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// Execution timer
|
2016-10-22 05:32:45 +02:00
|
|
|
void handle_timeout() noexcept; // Called by timer after requested time
|
|
|
|
struct timer timer;
|
2016-10-13 04:58:48 +02:00
|
|
|
|
2016-11-01 12:35:42 +01:00
|
|
|
// System target
|
|
|
|
struct star *star; // Registered by kernel
|
2016-10-31 20:12:43 +01:00
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// JSContext
|
2016-10-13 04:58:48 +02:00
|
|
|
operator JSContext *() const { return get(); }
|
|
|
|
operator JSContext &() const { return custom_ptr<JSContext>::operator*(); }
|
2016-10-14 02:24:50 +02:00
|
|
|
bool operator!() const { return !custom_ptr<JSContext>::operator bool(); }
|
|
|
|
auto &runtime() const { return our(JS_GetRuntime(get())); }
|
|
|
|
auto &runtime() { return our(JS_GetRuntime(get())); }
|
|
|
|
auto ptr() const { return get(); }
|
|
|
|
auto ptr() { return get(); }
|
2016-10-13 04:58:48 +02:00
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// BasicLockable // std::lock_guard<context>
|
|
|
|
void lock() { JS_BeginRequest(get()); }
|
|
|
|
void unlock() { JS_EndRequest(get()); }
|
|
|
|
|
2016-10-29 05:44:20 +02:00
|
|
|
context(struct runtime &, const struct opts &);
|
|
|
|
context(const struct opts &);
|
2016-10-12 09:04:26 +02:00
|
|
|
context() = default;
|
2016-10-20 12:25:41 +02:00
|
|
|
context(context &&) = delete;
|
2016-10-14 02:24:50 +02:00
|
|
|
context(const context &) = delete;
|
2016-10-16 01:53:44 +02:00
|
|
|
~context() noexcept;
|
2016-10-12 09:04:26 +02:00
|
|
|
};
|
|
|
|
|
2016-11-13 02:29:34 +01:00
|
|
|
// Current thread_local context. This value affects contextual data for almost every function
|
|
|
|
// in this entire subsystem (ircd::js). Located in ircd/js.cc
|
2016-10-16 01:53:44 +02:00
|
|
|
extern __thread context *cx;
|
|
|
|
|
2016-10-14 02:24:50 +02:00
|
|
|
// Get to our `struct context` from any upstream JSContext
|
|
|
|
const context &our(const JSContext *const &);
|
|
|
|
context &our(JSContext *const &);
|
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// Misc
|
2016-10-19 02:10:27 +02:00
|
|
|
inline auto running(const context &c) { return JS_IsRunning(c); }
|
2016-10-20 12:25:41 +02:00
|
|
|
inline auto version(const context &c) { return version(JS_GetVersion(c)); }
|
2016-11-13 02:29:34 +01:00
|
|
|
|
|
|
|
// Current stack
|
|
|
|
JS::Zone *current_zone(context & = *cx);
|
|
|
|
JSObject *current_global(context & = *cx);
|
|
|
|
JSCompartment *current_compartment(context & = *cx);
|
2016-10-20 12:25:41 +02:00
|
|
|
|
|
|
|
// Memory
|
2016-10-22 09:34:12 +02:00
|
|
|
void set(context &c, const JSGCParamKey &, const uint32_t &val);
|
|
|
|
uint32_t get(context &c, const JSGCParamKey &);
|
|
|
|
void out_of_memory(context &c);
|
|
|
|
void allocation_overflow(context &c);
|
2016-11-07 08:12:38 +01:00
|
|
|
bool run_gc(context &c) noexcept;
|
2016-10-20 04:50:55 +02:00
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
// Exception
|
|
|
|
bool pending_exception(const context &c);
|
|
|
|
void save_exception(context &c, const JSErrorReport &);
|
|
|
|
bool restore_exception(context &c);
|
|
|
|
bool report_exception(context &c);
|
|
|
|
|
|
|
|
// Interruption
|
2016-10-23 08:36:18 +02:00
|
|
|
bool interrupt(context &, const irq &);
|
2016-10-20 12:25:41 +02:00
|
|
|
bool interrupt_poll(const context &c);
|
|
|
|
|
|
|
|
// Execution
|
2016-10-29 05:44:20 +02:00
|
|
|
void restore_frame_chain(context &);
|
|
|
|
void save_frame_chain(context &);
|
2016-10-20 12:25:41 +02:00
|
|
|
void enter(context &); // throws if can't enter
|
|
|
|
void leave(context &); // must be called if enter() succeeds
|
|
|
|
|
|
|
|
// Enter JS within this closure. Most likely your function will return a `struct value`
|
|
|
|
// or JS::Value returned by most calls into JS.
|
|
|
|
template<class F> auto run(F&& function);
|
|
|
|
|
|
|
|
|
|
|
|
template<class F>
|
|
|
|
auto
|
|
|
|
run(F&& function)
|
|
|
|
{
|
2016-10-31 20:11:39 +01:00
|
|
|
assert(!pending_exception(*cx));
|
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
enter(*cx);
|
|
|
|
const scope out([]
|
|
|
|
{
|
|
|
|
leave(*cx);
|
|
|
|
});
|
2016-10-13 04:58:48 +02:00
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
return function();
|
|
|
|
}
|
2016-10-14 02:24:50 +02:00
|
|
|
|
2016-10-29 05:44:20 +02:00
|
|
|
inline void
|
|
|
|
save_frame_chain(context &c)
|
|
|
|
{
|
|
|
|
JS_SaveFrameChain(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
restore_frame_chain(context &c)
|
|
|
|
{
|
|
|
|
JS_RestoreFrameChain(c);
|
|
|
|
}
|
|
|
|
|
2016-10-20 12:25:41 +02:00
|
|
|
inline bool
|
|
|
|
report_exception(context &c)
|
2016-10-16 01:53:44 +02:00
|
|
|
{
|
2016-10-20 12:25:41 +02:00
|
|
|
return JS_ReportPendingException(c);
|
2016-10-16 01:53:44 +02:00
|
|
|
}
|
|
|
|
|
2016-10-20 04:50:55 +02:00
|
|
|
inline bool
|
2016-10-20 12:25:41 +02:00
|
|
|
pending_exception(const context &c)
|
2016-10-20 04:50:55 +02:00
|
|
|
{
|
2016-10-20 12:25:41 +02:00
|
|
|
return JS_IsExceptionPending(c);
|
|
|
|
}
|
|
|
|
|
2016-10-15 07:44:42 +02:00
|
|
|
inline JSObject *
|
2016-10-16 01:53:44 +02:00
|
|
|
current_global(context &c)
|
2016-10-15 07:44:42 +02:00
|
|
|
{
|
|
|
|
return JS::CurrentGlobalOrNull(c);
|
|
|
|
}
|
|
|
|
|
2016-10-30 13:51:22 +01:00
|
|
|
inline JSCompartment *
|
|
|
|
current_compartment(context &c)
|
|
|
|
{
|
|
|
|
return ::js::GetContextCompartment(c);
|
|
|
|
}
|
|
|
|
|
2016-11-13 02:29:34 +01:00
|
|
|
inline JS::Zone *
|
|
|
|
current_zone(context &c)
|
2016-10-13 04:58:48 +02:00
|
|
|
{
|
2016-11-13 02:29:34 +01:00
|
|
|
return ::js::GetContextZone(c);
|
2016-10-13 04:58:48 +02:00
|
|
|
}
|
|
|
|
|
2016-10-14 02:24:50 +02:00
|
|
|
inline context &
|
|
|
|
our(JSContext *const &c)
|
2016-10-13 04:58:48 +02:00
|
|
|
{
|
2016-10-14 02:24:50 +02:00
|
|
|
return *static_cast<context *>(JS_GetContextPrivate(c));
|
2016-10-13 04:58:48 +02:00
|
|
|
}
|
|
|
|
|
2016-10-14 02:24:50 +02:00
|
|
|
inline const context &
|
|
|
|
our(const JSContext *const &c)
|
2016-10-13 04:58:48 +02:00
|
|
|
{
|
2016-10-14 02:24:50 +02:00
|
|
|
return *static_cast<const context *>(JS_GetContextPrivate(const_cast<JSContext *>(c)));
|
2016-10-13 04:58:48 +02:00
|
|
|
}
|
|
|
|
|
2016-10-12 09:04:26 +02:00
|
|
|
} // namespace js
|
|
|
|
} // namespace ircd
|
2016-10-20 12:25:41 +02:00
|
|
|
|
|
|
|
static_assert(sizeof(struct ircd::js::context::state) == 8, "");
|