0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-18 10:53:48 +02:00

ircd::js: Upgrade SpiderMonkey to esr52 from esr45.

This commit is contained in:
Jason Volk 2017-08-23 15:25:22 -06:00
parent 1f4b21458f
commit 4d4a56fe9c
32 changed files with 2700 additions and 2010 deletions

7
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "boost"]
path = boost
url = https://github.com/boostorg/boost.git
[submodule "rocksdb"]
path = rocksdb
url = https://github.com/facebook/rocksdb.git
@ -8,3 +5,7 @@
path = gecko-dev
url = https://github.com/mozilla/gecko-dev.git
branch = esr45
[submodule "boost"]
path = boost
url = https://github.com/boostorg/boost.git
branch = boost-1.64.0

View file

@ -733,34 +733,36 @@ AC_HELP_STRING([--with-included-js[[[=shared]]]], [Use the JS engine (SpiderMonk
JS_LDFLAGS+=" -L$PWD/gecko-dev/js/src/build_OPT.OBJ/js/src"
AC_SUBST(JS_LIBS)
JS_LIBS+=" -lmozjs-45"
#JS_LIBS+=" -lmozglue"
JS_LIBS+=" -lmozjs-52"
JS_LIBS+=" -lmozglue"
dnl !!!!
dnl HACK BUG-WORKAROUND - Mozilla forgot to include this in their lib?
dnl Runtime segfault (unresolved dynamic function address) if this is not specified
JS_LIBS+=" $PWD/gecko-dev/js/src/build_OPT.OBJ/mfbt/Unified_cpp_mfbt0.o"
#JS_LIBS+=" $PWD/gecko-dev/js/src/build_OPT.OBJ/mfbt/Unified_cpp_mfbt0.o"
#JS_LIBS+=" $PWD/gecko-dev/js/src/build_OPT.OBJ/mfbt/Unified_cpp_mfbt1.o"
JS_LIBS+=" $PWD/gecko-dev/js/src/build_OPT.OBJ/mozglue/misc/TimeStamp.o"
AC_MSG_CHECKING([whether to use shared JS engine])
if [[ $withval = "shared" ]]; then
AC_MSG_RESULT([yes])
AC_MSG_NOTICE([Shared SpiderMonkey linkage requires running charybdis with an intact build directory])
js_linkage="shared"
JS_LDFLAGS+=" -Wl,-rpath -Wl,$PWD/gecko-dev/js/src/build_OPT.OBJ/dist/sdk/lib"
JS_LDFLAGS+=" -Wl,-rpath -Wl,$PWD/gecko-dev/js/src/build_OPT.OBJ/intl/icu/target/lib"
JS_LDFLAGS+=" -Wl,-rpath=$PWD/gecko-dev/js/src/build_OPT.OBJ/dist/sdk/lib"
JS_LDFLAGS+=" -Wl,-rpath=$PWD/gecko-dev/js/src/build_OPT.OBJ/intl/icu/target/lib"
else
AC_MSG_RESULT([no])
js_linkage="static"
JS_LIBS+=" -ljs_static"
fi
js_branch="esr45"
js_branch="esr52"
js_jobs="4"
if [[ $DEBUG ]]; then
js_options="--disable-optimize --enable-debug"
js_options="--disable-optimize --enable-debug --disable-js-shell"
else
js_options="--enable-optimize"
js_options="--enable-optimize --disable-js-shell"
fi
bash tools/buildjs.sh "$js_branch" "$js_options" "$js_jobs"
@ -774,17 +776,17 @@ AC_HELP_STRING([--with-included-js[[[=shared]]]], [Use the JS engine (SpiderMonk
with_included_js="no"
dnl TODO mumble mumble
AC_CHECK_LIB(mozjs-45, _Z11JS_ShutDownv,
AC_CHECK_LIB(mozjs-52, _Z11JS_ShutDownv,
[
], [
AC_MSG_WARN([Unable to find JS engine (SpiderMonkey 45) package. Try apt-get install libmozjs-dev])
AC_MSG_WARN([Unable to find JS engine (SpiderMonkey 52) package. Try apt-get install libmozjs-dev])
])
dnl TODO
dnl AC_SUBST(JS_CPPFLAGS, ["-I/usr/include/mozjs-45"])
dnl AC_SUBST(JS_CPPFLAGS, ["-I/usr/include/mozjs-52"])
dnl AC_SUBST(JS_LDFLAGS, [])
dnl AC_SUBST(JS_LIBS, ["-lmozjs-45"])
dnl AC_SUBST(JS_LIBS, ["-lmozjs-52"])
])
dnl TODO use an $enable_js var

@ -1 +1 @@
Subproject commit 37540f992d9a461d759fd0284515d39da103bea5
Subproject commit 090fadc2ca44cd8e2d057cb90cd648cf60587c2e

View file

@ -31,7 +31,7 @@ struct compartment
using closure = std::function<void (JSCompartment *)>;
private:
static void handle_iterate(JSRuntime *, void *, JSCompartment *) noexcept;
static void handle_iterate(JSContext *, void *, JSCompartment *) noexcept;
context *c;
JSCompartment *prev;
@ -62,9 +62,11 @@ struct compartment
static compartment &get();
};
// Get our structure from JSCompartment. Returns null when not ours.
const compartment *our(const JSCompartment *const &);
compartment *our(JSCompartment *const &);
// Compartment iterations
void for_each_compartment_our(const compartment::closure_our &); // iterate our compartments only
void for_each_compartment(const compartment::closure &); // iterate all compartments

View file

@ -29,7 +29,7 @@ namespace js {
enum class phase
:uint8_t
{
ACCEPT, // JS is not running.
LEAVE, // 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.
};
@ -45,44 +45,61 @@ enum class irq
TERMINATE, // The javascript should be terminated.
};
struct context
:private custom_ptr<JSContext>
class context
:custom_ptr<JSContext>
{
// Context options
struct opts
{
size_t stack_chunk_size = 8_KiB;
microseconds timer_limit = 10s;
}
opts;
// JS engine callback surface.
static void handle_error(JSContext *, JSErrorReport *) noexcept;
static bool handle_interrupt(JSContext *) noexcept;
static void handle_timeout(JSContext *) noexcept;
static void handle_telemetry(int id, uint32_t sample, const char *key) noexcept;
static bool handle_get_performance_groups(JSContext *, ::js::PerformanceGroupVector &, void *) noexcept;
static bool handle_stopwatch_commit(uint64_t, ::js::PerformanceGroupVector &, void *) noexcept;
static bool handle_stopwatch_start(uint64_t, void *) noexcept;
static void handle_out_of_memory(JSContext *, void *) noexcept;
static void handle_large_allocation_failure(void *) noexcept;
static void handle_trace_gray(JSTracer *, void *) noexcept;
static void handle_trace_extra(JSTracer *, void *) noexcept;
static void handle_weak_pointer_zone(JSContext *, void *) noexcept;
static void handle_weak_pointer_compartment(JSContext *, JSCompartment *, void *) noexcept;
static void handle_zone_sweep(JS::Zone *) noexcept;
static void handle_zone_destroy(JS::Zone *) noexcept;
static void handle_compartment_name(JSContext *, JSCompartment *, char *buf, size_t) noexcept;
static void handle_compartment_destroy(JSFreeOp *, JSCompartment *) noexcept;
static void handle_finalize(JSFreeOp *, JSFinalizeStatus, bool is_compartment, void *) noexcept;
static void handle_objects_tenured(JSContext *, void *) noexcept;
static void handle_slice(JSContext *, JS::GCProgress, const JS::GCDescription &) noexcept;
static void handle_gc(JSContext *, JSGCStatus, void *) noexcept;
static bool handle_preserve_wrapper(JSContext *, JSObject *) noexcept;
static void handle_activity(void *priv, bool active) noexcept;
static bool handle_promise_enqueue_job(JSContext *, JS::HandleObject, JS::HandleObject, JS::HandleObject, void *) noexcept;
static void handle_promise_rejection_tracker(JSContext *, JS::HandleObject, PromiseRejectionHandlingState, void *) noexcept;
static bool handle_start_async_task(JSContext *, JS::AsyncTask *) noexcept;
static bool handle_finish_async_task(JS::AsyncTask *) noexcept;
static JSObject *handle_get_incumbent_global(JSContext *) noexcept;
// Exception state
JSExceptionState *except; // Use save_exception()/restore_exception()
// Interruption state
public:
struct opts;
struct alignas(8) state
{
uint32_t sem;
enum phase phase;
enum irq irq;
};
std::unique_ptr<struct opts> opts; // Options for the context.
std::thread::id tid; // This is recorded for assertions/logging.
struct tracing tracing; // State for garbage collection / tracing.
JSExceptionState *except; // Use save_exception()/restore_exception()
std::atomic<struct state> state; // Atomic state of execution
std::function<int (const irq &)> on_intr; // User interrupt hook (ret -1 to not interfere)
bool handle_interrupt(); // Called by runtime on interrupt
// Execution timer
void handle_timeout() noexcept; // Called by timer after requested time
struct timer timer;
// System target
struct star *star; // Registered by kernel
struct timer timer; // Preemption timer
struct star *star; // System target
// JSContext
operator JSContext *() const { return get(); }
operator JSContext &() const { return custom_ptr<JSContext>::operator*(); }
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(); }
@ -90,19 +107,37 @@ struct context
void lock() { JS_BeginRequest(get()); }
void unlock() { JS_EndRequest(get()); }
context(struct runtime &, const struct opts &);
context(const struct opts &);
context(std::unique_ptr<struct opts> opts, JSContext *const &parent = nullptr);
context(const struct opts &, JSContext *const &parent = nullptr);
context() = default;
context(context &&) = delete;
context(const context &) = delete;
~context() noexcept;
};
// Options for the context. Most of these values will never change from what
// the user initially specified, but this is not held as const by the context to
// allow for JS code itself to change its own options if possible.
struct context::opts
{
size_t max_bytes = 64_MiB;
size_t max_nursery_bytes = 16_MiB;
size_t code_stack_max = 0;
size_t trusted_stack_max = 0;
size_t untrusted_stack_max = 0;
size_t stack_chunk_size = 8_KiB;
microseconds timer_limit = 10s; //TODO: Temp
bool concurrent_parsing = true;
bool concurrent_jit = true;
uint8_t gc_zeal_mode = 0; // normal
uint32_t gc_zeal_freq = JS_DEFAULT_ZEAL_FREQ; // 100
};
// Current thread_local context. This value affects contextual data for almost every function
// in this entire subsystem (ircd::js). Located in ircd/js.cc
// in this entire subsystem (ircd::js). Located in ircd/js.cc.
extern __thread context *cx;
// Get to our `struct context` from any upstream JSContext
// Get to our own `struct context` from any upstream JSContext*
const context &our(const JSContext *const &);
context &our(JSContext *const &);
@ -110,7 +145,7 @@ context &our(JSContext *const &);
inline auto running(const context &c) { return JS_IsRunning(c); }
inline auto version(const context &c) { return version(JS_GetVersion(c)); }
// Current stack
// Current
JS::Zone *current_zone(context & = *cx);
JSObject *current_global(context & = *cx);
JSCompartment *current_compartment(context & = *cx);
@ -120,27 +155,25 @@ 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);
bool maybe_gc(context &c) noexcept;
bool run_gc(context &c) noexcept;
// Exception
bool pending_exception(const context &c);
void save_exception(context &c);
void clear_exception(context &c);
void save_exception(context &c);
bool restore_exception(context &c);
bool report_exception(context &c);
// Interruption
bool interrupt(context &, const irq &);
bool interrupt_poll(const context &c);
// Execution
void restore_frame_chain(context &);
void save_frame_chain(context &);
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.
// (Convenience) 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);
@ -159,24 +192,6 @@ run(F&& function)
return function();
}
inline void
save_frame_chain(context &c)
{
JS_SaveFrameChain(c);
}
inline void
restore_frame_chain(context &c)
{
JS_RestoreFrameChain(c);
}
inline bool
report_exception(context &c)
{
return JS_ReportPendingException(c);
}
inline void
clear_exception(context &c)
{

View file

@ -1,65 +0,0 @@
/*
* 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_CONTRACT_H
namespace ircd {
namespace js {
// contract is a promise from C to a future in JS. When a task has no more pending contracts,
// it ceases to exist.
//
// * The promise is the `struct contract` instantiated in C.
// * The future is returned into JS as an object trapped by "future" (future.so)
// * C unconditionally enters the closure to report result.
// * * if the C closure returns a `value` the "value" field is set in JS
// * * if the C closure throws an exception the "error" field conveys the exception in JS
// *
// * "id": uint
// * "value": object [optional]
// * "error": exception object [optional]
//
struct contract
:std::weak_ptr<struct task>
{
using closure = std::function<value ()>;
object future;
operator const object &() const { return future; }
operator object &() { return future; }
operator value() const { return future; }
// Enter this closure and return or throw to complete the result contract.
void operator()(const closure &) noexcept;
// The future object is constructed using the "future" trap.
// The task is automatically found with task::get() in the overload without a task argument.
contract(struct task &, object::handle future);
contract(object::handle future);
contract(contract &&) = default;
contract(const contract &) = default;
~contract() noexcept;
};
} // namespace js
} // namespace ircd

View file

@ -26,6 +26,7 @@ namespace ircd {
namespace js {
// Returns static string
const char *reflect(const jstype &);
const char *reflect(const JSType &);
const char *reflect(const JSExnType &);
const char *reflect(const JSGCMode &);
@ -33,10 +34,10 @@ const char *reflect(const JSGCStatus &);
const char *reflect(const JS::GCProgress &);
const char *reflect(const JSGCParamKey &);
const char *reflect(const JSFinalizeStatus &);
const char *reflect(const JSContextOp &);
const char *reflect(const JS::PromiseState &);
const char *reflect(const PromiseRejectionHandlingState &);
const char *reflect_prop(const uint &flag);
const char *reflect_telemetry(const int &id);
const char *reflect(const jstype &);
// Returns single-line string
std::string debug(const JS::Value &);
@ -54,6 +55,7 @@ void dump(const JSContext *v);
void dump(const JSScript *const &v);
void dump(const char16_t *const &v, const size_t &len);
void dump(const ::js::InterpreterFrame *v);
void dump_promise(const JS::HandleObject &promise);
void backtrace();
// writes lines to ircd::js::log

View file

@ -35,6 +35,9 @@ object enclosing_scope(JSFunction *const &);
struct function
:root<JSFunction *>
{
struct native; // Use this instead to specify a function in C
struct literal; // Use this instead to supply the JS as a C string literal.
IRCD_OVERLOAD(outermost_enclosing)
operator JSObject *() const;
@ -104,10 +107,10 @@ function::function(outermost_enclosing_t)
template<class string_t>
function::function(JS::AutoObjectVector &stack,
const JS::CompileOptions &opts,
const char *const &name,
const std::vector<string_t> &args,
const string_t &src)
const JS::CompileOptions &opts,
const char *const &name,
const std::vector<string_t> &args,
const string_t &src)
:function::root::type{}
{
std::vector<const typename string_t::value_type *> argp(args.size());
@ -174,7 +177,7 @@ const
inline object
enclosing_scope(JSFunction *const &f)
{
return ::js::GetNearestEnclosingWithScopeObjectForFunction(f);
return ::js::GetNearestEnclosingWithEnvironmentObjectForFunction(f);
}
inline bool

View file

@ -25,7 +25,7 @@
namespace ircd {
namespace js {
class function_literal
class function::literal
:public root<JSFunction *>
{
const char *name;
@ -33,19 +33,16 @@ class function_literal
std::vector<const char *> prototype;
public:
function_literal(const char *const &name,
const std::initializer_list<const char *> &prototype,
const char *const &text);
literal(const char *const &name,
const std::initializer_list<const char *> &prototype,
const char *const &text);
function_literal(function_literal &&) noexcept;
function_literal(const function_literal &) = delete;
literal(literal &&) noexcept;
literal(const literal &) = delete;
};
inline function_literal
operator ""_function(const char *const text, const size_t len)
{
return { "<literal>", {}, text };
}
// Compile a C++ string literal as an actual JavaScript function.
function::literal operator ""_function(const char *const text, const size_t len);
} // namespace js
} // namespace ircd

View file

@ -0,0 +1,78 @@
/*
* 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_FUNCTION_NATIVE
namespace ircd {
namespace js {
struct function::native
:root<JSFunction *>
{
using closure = std::function<value (object::handle, value::handle, const args &)>;
const char *_name;
closure lambda;
protected:
virtual value on_call(object::handle callee, value::handle that, const args &);
virtual value on_new(object::handle callee, const args &);
private:
static function::native &from(JSObject *const &);
static bool handle_call(JSContext *, unsigned, JS::Value *) noexcept;
public:
uint16_t arity() const;
string display_name() const;
string name() const;
value operator()(const object::handle &, const vector<value>::handle &) const;
template<class... args> value operator()(const object::handle &, args&&...) const;
native(const char *const &name = "<native>",
const uint &flags = 0,
const uint &arity = 0,
const closure & = {});
native(native &&) = delete;
native(const native &) = delete;
virtual ~native() noexcept;
};
template<class... args>
value
function::native::operator()(const object::handle &that,
args&&... a)
const
{
const vector<value> argv
{
std::forward<args>(a)...
};
const vector<value>::handle handle(argv);
return operator()(that, handle);
}
} // namespace js
} // namespace ircd

View file

@ -30,10 +30,15 @@ class global
{
static void handle_trace(JSTracer *, JSObject *) noexcept;
std::map<std::string, module *> imports;
object import(module &importer, const string &requesting, const object &that);
std::unique_ptr<function::native> module_resolve_hook;
public:
global(trap &,
JSPrincipals *const & = nullptr,
JS::CompartmentOptions = JS::CompartmentOptions());
JS::CompartmentCreationOptions = {},
JS::CompartmentBehaviors = {});
global(global &&) = default;
global(const global &) = delete;

View file

@ -39,7 +39,7 @@
// From a completely clean build, configuring IRCd with --enable-debug should compile SpiderMonkey
// in debug as well.
#ifdef RB_DEBUG
// #define DEBUG
#define DEBUG
#endif
// SpiderMonkey headers require an include basis e.g. -I/usr/include/mozjs-XX as their
@ -66,13 +66,12 @@ using ircd::operator<<;
#include "version.h"
#include "type.h"
#include "debug.h"
#include "tracing.h"
#include "runtime.h"
#include "timer.h"
#include "context.h"
#include "priv.h"
#include "compartment.h"
#include "debug.h"
#include "root.h"
#include "error.h"
#include "native.h"
@ -86,21 +85,21 @@ using ircd::operator<<;
#include "set.h"
#include "del.h"
#include "vector.h"
#include "for_each.h"
#include "xdr.h"
#include "script.h"
#include "module.h"
#include "args.h"
#include "function.h"
#include "function_literal.h"
#include "function_native.h"
#include "call.h"
#include "for_each.h"
#include "args.h"
#include "trap.h"
#include "trap_function.h"
#include "trap_property.h"
#include "ctor.h"
#include "generator.h"
#include "global.h"
#include "contract.h"
#include "star.h"
#include "task.h"
#endif // defined(RB_ENABLE_JS)

147
include/ircd/js/module.h Normal file
View file

@ -0,0 +1,147 @@
/*
* 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_MODULE_H
namespace ircd {
namespace js {
struct module
:object
,script
{
IRCD_OVERLOAD(yielding)
struct trap *trap;
object requested() const; // GetRequestedModules()
void instantiate(); // ModuleDeclarationInstantiation()
void operator()() const; // ModuleEvaluation()
module(yielding_t, const JS::ReadOnlyCompileOptions &, const std::u16string &src, struct trap *const & = nullptr, const bool &instantiate = true);
module(const JS::ReadOnlyCompileOptions &, const std::u16string &src, struct trap *const & = nullptr, const bool &instantiate = true);
static module &our(const object &module); // Get this structure from the record object
};
inline
module::module(const JS::ReadOnlyCompileOptions &opts,
const std::u16string &src,
struct trap *const &trap,
const bool &instantiate)
:object{[this, &opts, &src, &instantiate]
() -> object
{
static const auto ownership(JS::SourceBufferHolder::NoOwnership);
object ret(object::uninitialized);
JS::SourceBufferHolder buf(src.data(), src.size(), ownership);
if(!JS::CompileModule(*cx, opts, buf, &ret))
throw jserror(jserror::pending);
if(unlikely(!ret.get()))
throw jserror(jserror::pending);
JS::SetModuleHostDefinedField(ret, value(value::pointer, this));
return ret;
}()}
,script
{
JS::GetModuleScript(*cx, static_cast<object &>(*this))
}
,trap{trap}
{
if(instantiate)
this->instantiate();
}
// This constructor compiles the module concurrently by yielding this ircd::ctx.
// See the yielding_t overload in script.h for details.
inline
module::module(yielding_t,
const JS::ReadOnlyCompileOptions &opts,
const std::u16string &src,
struct trap *const &trap,
const bool &instantiate)
:object{[this, &opts, &src]
() -> object
{
static const auto ownership(JS::SourceBufferHolder::NoOwnership);
auto future(compile_async(opts, src, true));
void *const token(future.get());
object ret(object::uninitialized);
if(!token)
{
JS::SourceBufferHolder buf(src.data(), src.size(), ownership);
if(!JS::CompileModule(*cx, opts, buf, &ret))
throw jserror(jserror::pending);
}
else ret = JS::FinishOffThreadModule(*cx, token);
if(unlikely(!ret.get()))
throw jserror(jserror::pending);
JS::SetModuleHostDefinedField(ret, pointer_value(this));
return ret;
}()}
,script
{
JS::GetModuleScript(*cx, static_cast<object &>(*this))
}
,trap{trap}
{
if(instantiate)
this->instantiate();
}
inline void
module::instantiate()
{
if(!JS::ModuleDeclarationInstantiation(*cx, static_cast<object &>(*this)))
throw jserror(jserror::pending);
}
inline void
module::operator()()
const
{
if(!JS::ModuleEvaluation(*cx, static_cast<const object &>(*this)))
throw jserror(jserror::pending);
}
inline object
module::requested()
const
{
return JS::GetRequestedModules(*cx, static_cast<const object &>(*this));
}
inline module &
module::our(const object &module)
{
const value priv(JS::GetModuleHostDefinedField(module));
return *pointer_value<struct module>(priv);
}
} // namespace js
} // namespace ircd

View file

@ -19,6 +19,11 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
//
// TODO: This file will be merged with string.h.
// Don't confuse it with SpiderMonkey terminology for "JSNative" functions
//
#pragma once
#define HAVE_IRCD_JS_NATIVE_H

View file

@ -88,6 +88,7 @@ struct object
object(const value &);
object(JSObject *const &);
object(JSObject &);
object(nullptr_t);
object(uninitialized_t);
object();
@ -111,6 +112,12 @@ object::object(uninitialized_t)
{
}
inline
object::object(nullptr_t)
:object{value{nullptr}}
{
}
inline
object::object(JSObject &obj)
:object::root::type{&obj}
@ -231,7 +238,7 @@ object::object(const uint8_t *const &bytecode,
const size_t &size)
:object::root::type
{
JS_DecodeInterpretedFunction(*cx, bytecode, size)
//JS_DecodeInterpretedFunction(*cx, bytecode, size)
}
{
if(unlikely(!this->get()))
@ -297,20 +304,11 @@ const
return ret;
}
inline
object::operator JSString *()
const
{
assert(this->get());
return JS_BasicObjectToString(*cx, *this);
}
inline
object::operator value()
const
{
assert(this->get());
return JS::ObjectValue(*this->get());
return static_cast<JS::Value>(*this);
}
inline
@ -321,6 +319,14 @@ const
return JS::ObjectValue(*this->get());
}
inline
object::operator JSString *()
const
{
assert(this->get());
return JS_BasicObjectToString(*cx, *this);
}
inline size_t
bytecodes(const object_handle &obj,
uint8_t *const &buf,
@ -329,7 +335,7 @@ bytecodes(const object_handle &obj,
uint32_t ret;
const custom_ptr<void> ptr
{
JS_EncodeInterpretedFunction(*cx, obj, &ret), js_free
//JS_EncodeInterpretedFunction(*cx, obj, &ret), js_free
};
const auto cpsz(std::min(size_t(ret), size));

View file

@ -89,15 +89,15 @@ struct root
tracing::list::iterator add(base_type *const &ptr)
{
return rt->tracing.heap.emplace(end(rt->tracing.heap), tracing::thing { ptr, js::type<T>() });
return cx->tracing.heap.emplace(end(cx->tracing.heap), tracing::thing { ptr, js::type<T>() });
}
void del(const tracing::list::iterator &tracing_it)
{
if(tracing_it != end(rt->tracing.heap))
if(tracing_it != end(cx->tracing.heap))
{
const void *ptr = tracing_it->ptr;
rt->tracing.heap.erase(tracing_it);
cx->tracing.heap.erase(tracing_it);
}
}
@ -130,7 +130,7 @@ struct root
:base_type{other.get()}
,tracing_it{std::move(other.tracing_it)}
{
other.tracing_it = end(rt->tracing.heap);
other.tracing_it = end(cx->tracing.heap);
tracing_it->ptr = static_cast<base_type *>(this);
}

View file

@ -1,154 +0,0 @@
/*
* 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_RUNTIME_H
namespace ircd {
namespace js {
class runtime
{
static void handle_error(JSContext *, const char *msg, JSErrorReport *) noexcept;
static void handle_out_of_memory(JSContext *, void *) noexcept;
static void handle_large_allocation_failure(void *) noexcept;
static void handle_telemetry(int id, uint32_t sample, const char *key) noexcept;
static void handle_finalize(JSFreeOp *, JSFinalizeStatus, bool is_compartment, void *) noexcept;
static void handle_trace_gray(JSTracer *, void *) noexcept;
static void handle_trace_extra(JSTracer *, void *) noexcept;
static void handle_weak_pointer_zone(JSRuntime *, void *) noexcept;
static void handle_weak_pointer_compartment(JSRuntime *, JSCompartment *, void *) noexcept;
static void handle_slice(JSRuntime *, JS::GCProgress, const JS::GCDescription &) noexcept;
static void handle_zone_sweep(JS::Zone *) noexcept;
static void handle_zone_destroy(JS::Zone *) noexcept;
static void handle_compartment_name(JSRuntime *, JSCompartment *, char *buf, size_t) noexcept;
static void handle_compartment_destroy(JSFreeOp *, JSCompartment *) noexcept;
static void handle_gc(JSRuntime *, JSGCStatus, void *) noexcept;
static bool handle_preserve_wrapper(JSContext *, JSObject *) noexcept;
static bool handle_context(JSContext *, uint op, void *) noexcept;
static void handle_activity(void *priv, bool active) noexcept;
static bool handle_interrupt(JSContext *) noexcept;
public:
struct opts
{
size_t max_bytes = 64_MiB;
size_t max_nursery_bytes = 16_MiB;
size_t code_stack_max = 0;
size_t trusted_stack_max = 0;
size_t untrusted_stack_max = 0;
};
struct opts opts; // We keep a copy of the given opts here
std::thread::id tid; // This is recorded for assertions/logging.
struct tracing tracing; // State for garbage collection / tracing.
custom_ptr<JSRuntime> _ptr;
operator JSRuntime *() const { return _ptr.get(); }
operator JSRuntime &() const { return *_ptr; }
bool operator!() const { return !_ptr; }
auto get() const { return _ptr.get(); }
auto get() { return _ptr.get(); }
runtime(const struct opts &, runtime *const &parent = nullptr);
runtime() = default;
runtime(runtime &&) noexcept;
runtime(const runtime &) = delete;
runtime &operator=(runtime &&) noexcept;
runtime &operator=(const runtime &) = delete;
~runtime() noexcept;
friend void interrupt(runtime &);
};
// Current thread_local runtime. This value affects contextual data for almost every function
// in this entire subsystem (ircd::js). Located in ircd/js.cc
extern __thread runtime *rt;
// Get to our `struct runtime` from any upstream JSRuntime
const runtime &our(const JSRuntime *const &);
runtime &our(JSRuntime *const &);
// Get to our runtime from any JSObject
const runtime &our_runtime(const JSObject &);
runtime &our_runtime(JSObject &);
// Get to our runtime from any JSFreeOp
JSFreeOp *default_freeop(runtime &);
const runtime &our_runtime(const JSFreeOp &);
runtime &our_runtime(JSFreeOp &);
// Do not call interrupt() unless you know what you're doing; see context.h.
void interrupt(runtime &r);
bool run_gc(runtime &r) noexcept;
inline void
interrupt(runtime &r)
{
JS_RequestInterruptCallback(r);
}
inline runtime &
our_runtime(JSFreeOp &o)
{
return our(o.runtime());
}
inline const runtime &
our_runtime(const JSFreeOp &o)
{
return our(o.runtime());
}
inline JSFreeOp *
default_freeop(runtime &r)
{
return JS_GetDefaultFreeOp(r);
}
inline runtime &
our_runtime(JSObject &o)
{
return our(JS_GetObjectRuntime(&o));
}
inline const runtime &
our_runtime(const JSObject &o)
{
return our(JS_GetObjectRuntime(const_cast<JSObject *>(&o)));
}
inline runtime &
our(JSRuntime *const &c)
{
return *static_cast<runtime *>(JS_GetRuntimePrivate(c));
}
inline const runtime &
our(const JSRuntime *const &c)
{
return *static_cast<const runtime *>(JS_GetRuntimePrivate(const_cast<JSRuntime *>(c)));
}
} // namespace js
} // namespace ircd

View file

@ -25,7 +25,7 @@
namespace ircd {
namespace js {
ctx::future<void *> compile_async(const JS::ReadOnlyCompileOptions &, const std::u16string &);
ctx::future<void *> compile_async(const JS::ReadOnlyCompileOptions &, const std::u16string &, const bool &module = false);
string decompile(const JS::Handle<JSScript *> &, const char *const &name, const bool &pretty = false);
size_t bytecodes(const JS::Handle<JSScript *> &, uint8_t *const &buf, const size_t &max);
bool compilable(const char *const &str, const size_t &len, const object &stack = {});
@ -36,7 +36,8 @@ struct script
{
IRCD_OVERLOAD(yielding)
value operator()(JS::AutoObjectVector &stack) const;
value operator()(JS::AutoObjectVector &environment) const;
value operator()(const object &environment) const;
value operator()() const;
using root<JSScript *>::root;
@ -67,7 +68,7 @@ script::script(const uint8_t *const &bytecode,
const size_t &size)
:script::root::type
{
JS_DecodeScript(*cx, bytecode, size)
//JS_DecodeScript(*cx, bytecode, size)
}
{
if(unlikely(!this->get()))
@ -92,18 +93,19 @@ script::script(const JS::ReadOnlyCompileOptions &opts,
throw jserror(jserror::pending);
}
// This constructor compiles the script concurrently by yielding this ircd::ctx.
// The compilation occurs on another thread entirely, so other ircd contexts will
// still be able to run, but this ircd context will block until the script is
// compiled at which point this constructor will complete.
inline
script::script(yielding_t,
const JS::ReadOnlyCompileOptions &opts,
const std::u16string &src)
:script::root::type{[&opts, &src]
:script::root::type{[this, &opts, &src]
{
// This constructor compiles the script concurrently by yielding this ircd::ctx.
// The compilation occurs on another thread entirely, so other ircd contexts will
// still be able to run.
auto future(compile_async(opts, src));
void *const token(future.get());
return token? JS::FinishOffThreadScript(*cx, *rt, token):
return token? JS::FinishOffThreadScript(*cx, token):
script(opts, src).get();
}()}
{
@ -123,11 +125,20 @@ const
}
inline value
script::operator()(JS::AutoObjectVector &stack)
script::operator()(const object &environment)
const
{
JS::AutoObjectVector env(*cx);
env.append(object());
return operator()(env);
}
inline value
script::operator()(JS::AutoObjectVector &environment)
const
{
value ret;
if(!JS_ExecuteScript(*cx, stack, *this, &ret))
if(!JS_ExecuteScript(*cx, environment, *this, &ret))
throw jserror(jserror::pending);
return ret;

View file

@ -1,43 +0,0 @@
/*
* 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_STAR_H
namespace ircd {
namespace js {
struct star
{
// Active processes by PID. This is managed by the actual task structure itself.
// A task pointer in the map will always be valid. When the last shared_ptr to a task is
// destroyed the task removes itself and weak references to the task are invalidated.
std::map<uint64_t, task *> tasks;
// Modules conducting some asynchronous operation on behalf of a task maintain state with
// a `struct contract` object. When the result is ready and the contract is fulfilled
// the contract object inserts itself into this queue (the user does not do this).
// The kernel is listening on the other end of this queue.
ctx::queue<contract> completion;
};
} // namespace js
} // namespace ircd

View file

@ -57,6 +57,7 @@ struct string
operator std::string() const;
operator std::u16string() const;
operator JS::Value() const;
operator value() const;
using root<JSString *>::root;
@ -110,10 +111,13 @@ template<class A, class B> string_comparison<A, B> operator==(const A &a, const
template<class A, class B> string_comparison<A, B> operator!=(const A &a, const B &b);
using string_pair = std::pair<string, string>;
string_pair splita(const string &s, const char16_t &c);
string_pair splita(const string &s, const char &c); // split() but skips multiple contiguous c
string_pair split(const string &s, const char16_t &c); // split on first position of c
string_pair split(const string &s, const char &c);
string_pair split(const string &s, const char16_t &c);
string substr(const string &s, const size_t &pos, const size_t &len);
string operator+(const string &left, const string &right);
string &operator+=(string &left, const string &right);
using string_closure = std::function<void (const string &)>;
void tokens(const string &, const char &sep, const string_closure &);
@ -122,7 +126,7 @@ inline
string::string()
:string::root::type
{
JS_GetEmptyString(*rt)
JS_GetEmptyString(*cx)
}
{
}
@ -169,7 +173,7 @@ string::string(const char *const &s,
:string::root::type{[&s, &len]
{
if(!s || !*s)
return JS_GetEmptyString(*rt);
return JS_GetEmptyString(*cx);
auto buf(native_external_copy(s, len));
return JS_NewExternalString(*cx, buf.release(), len, &native_external_delete);
@ -197,7 +201,7 @@ string::string(const char16_t *const &s,
:string::root::type{[&s, &len]
{
if(!s || !*s)
return JS_GetEmptyString(*rt);
return JS_GetEmptyString(*cx);
// 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.
@ -218,7 +222,7 @@ string::string(literal_t,
{
s && *s?
JS_NewExternalString(*cx, s, std::char_traits<char16_t>::length(s), &native_external_static):
JS_GetEmptyString(*rt)
JS_GetEmptyString(*cx)
}
{
if(unlikely(!this->get()))
@ -271,6 +275,13 @@ const
inline
string::operator value()
const
{
return static_cast<JS::Value>(*this);
}
inline
string::operator JS::Value()
const
{
return JS::StringValue(this->get());
}
@ -339,7 +350,7 @@ tokens(const string &str,
const char &sep,
const string_closure &closure)
{
for(auto pair(split(str, sep));; pair = split(pair.second, sep))
for(auto pair(splita(str, sep));; pair = splita(pair.second, sep))
{
closure(pair.first);
if(pair.second.empty())
@ -354,16 +365,41 @@ split(const string &s,
return split(s, char16_t(c));
}
inline std::pair<string, string>
splita(const string &s,
const char &c)
{
return splita(s, char16_t(c));
}
inline std::pair<string, string>
split(const string &s,
const char16_t &c)
{
size_t i(0);
for(; i < size(s) && at(s, i) != c; ++i);
size_t a(0);
for(; a < size(s) && at(s, a) != c; ++a);
return
{
substr(s, 0, i),
i < size(s)? substr(s, i + 1, size(s) - i) : string()
substr(s, 0, a),
a + 1 < size(s)? substr(s, a + 1, size(s) - a) : string()
};
}
inline std::pair<string, string>
splita(const string &s,
const char16_t &c)
{
size_t a(0);
for(; a < size(s) && at(s, a) != c; ++a);
size_t b(a);
for(; b < size(s) && at(s, b) == c; ++b);
return
{
substr(s, 0, a),
b < size(s)? substr(s, b, size(s) - b) : string()
};
}
@ -380,6 +416,14 @@ substr(const string &s,
return ret;
}
inline string &
operator+=(string &left,
const string &right)
{
left = operator+(left, right);
return left;
}
inline string
operator+(const string &left,
const string &right)

View file

@ -29,24 +29,10 @@ struct task
:std::enable_shared_from_this<task>
{
uint64_t pid; // unique process ID
uint64_t yid; // ID of current yield attempting unification
std::map<uint64_t, object> complete; // futures waiting for yield unification
std::map<uint64_t, object> pending; // pending futures awaiting results
std::shared_ptr<task> work; // references self when there is unfinished work
struct global global; // global / this / root scope object
script main; // main generator wrapper script
struct generator generator; // generator state
bool canceling = false;
private:
static uint64_t tasks_next_pid();
uint64_t tasks_insert();
bool tasks_remove();
struct module main; // main module script
public:
bool pending_add(const uint64_t &id, object);
bool pending_del(const uint64_t &id);
task(const std::u16string &source);
task(const std::string &source);
task(task &&) = delete;

View file

@ -27,26 +27,20 @@ namespace js {
struct trap
{
struct property;
struct function;
struct property; // Properties (trap_property.h)
struct function; // Functions (trap_function.h)
struct constint; // Constant integers (trap_constint.h)
trap *parent;
const std::string _name; // don't touch
std::map<std::string, property *> member;
std::map<std::string, function *> memfun;
std::map<std::string, trap *> children;
std::string name; // classp name
trap *prototrap; // Parent trap
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 specifications
std::map<std::string, property *> sprop;
std::map<std::string, function *> sfunc;
static trap &from(const JSObject &);
static trap &from(const JSObject *const &);
// Member specifications
std::map<std::string, property *> memprop;
std::map<std::string, function *> memfunc;
protected:
void debug(const void *const &that, const char *fmt, ...) const AFP(3, 4);
@ -64,8 +58,6 @@ struct trap
virtual void on_gc(JSObject *const &);
private: protected:
void add_this();
void del_this();
void host_exception(const void *const &that, const char *fmt, ...) const AFP(3, 4);
// Internal callback interface
@ -83,18 +75,17 @@ struct trap
static bool handle_ctor(JSContext *, unsigned argc, JS::Value *argv) noexcept;
static void handle_dtor(JSFreeOp *, JSObject *) noexcept;
// Aggregate structure specifying internal callback surface
static const JSClassOps cops; // Used for regular objects
static const JSClassOps gcops; // Used for global objects
const JSClass classp; // Instance uses one of the above JSClassOps
public:
auto &name() const { return _name; }
auto &jsclass() const { return *_class; }
auto &jsclass() { return *_class; }
static trap &from(const JSObject *const &);
static trap &from(const JSObject &);
// Get child by name (NOT PATH)
const trap &child(const std::string &name) const;
trap &child(const std::string &name);
// Path is absolute to root
static trap &find(const string::handle &path);
static trap &find(const std::string &path);
auto &jsclass() const { return classp; }
auto &jsclass() { return classp; }
operator const JSClass &() const { return jsclass(); }
operator const JSClass *() const { return &jsclass(); }
@ -104,15 +95,12 @@ struct trap
object construct(const vector<value>::handle &argv = {});
template<class... args> object operator()(args&&...);
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;
};
extern __thread trap *tree;
} // namespace js
} // namespace ircd
@ -120,6 +108,10 @@ template<class... args>
ircd::js::object
ircd::js::trap::operator()(args&&... a)
{
vector<value> argv{std::forward<args>(a)...};
const vector<value> argv
{
std::forward<args>(a)...
};
return construct(argv);
}

View file

@ -27,13 +27,13 @@ namespace js {
struct trap::function
{
friend struct trap;
using closure = std::function<value (object::handle, value::handle, const args &)>;
trap *member;
const std::string name;
const uint flags;
const uint arity;
struct trap *trap;
std::string name;
closure lambda;
JSFunctionSpec spec;
protected:
virtual value on_call(object::handle callee, value::handle that, const args &);
@ -41,15 +41,16 @@ struct trap::function
private:
static function &from(JSObject *const &);
static bool handle_call(JSContext *, unsigned, JS::Value *) noexcept;
public:
js::function operator()(const object::handle &) const;
function(trap &,
std::string name,
const uint &flags = 0,
const uint &arity = 0,
function(struct trap &,
const std::string &name,
const uint16_t &flags = 0,
const uint16_t &arity = 0,
const closure & = {});
function(function &&) = delete;

View file

@ -27,10 +27,12 @@ namespace js {
struct trap::property
{
friend struct trap;
using function = js::function;
struct trap *trap;
const std::string name;
std::string name;
JSPropertySpec spec;
virtual value on_set(function::handle, object::handle, value::handle);
virtual value on_get(function::handle, object::handle);
@ -40,7 +42,7 @@ struct trap::property
static bool handle_get(JSContext *c, unsigned argc, JS::Value *argv) noexcept;
public:
property(struct trap &, std::string name);
property(struct trap &, const std::string &name, const uint &flags = 0);
property(property &&) = delete;
property(const property &) = delete;
virtual ~property() noexcept;

View file

@ -45,7 +45,7 @@ template<> constexpr jstype type<JSString *>() { return jstype::STRING;
template<> constexpr jstype type<JS::Symbol *>() { return jstype::SYMBOL; }
template<> constexpr jstype type<jsid>() { return jstype::ID; }
inline jstype // This cannot be constexpr <= GCC-4.9
constexpr jstype
type(const JSType &t)
{
switch(t)
@ -59,8 +59,9 @@ type(const JSType &t)
case JSTYPE_BOOLEAN: return jstype::VALUE;
case JSTYPE_NULL: return jstype::VALUE;
case JSTYPE_LIMIT: return jstype::VALUE;
default: return jstype::VALUE;
}
return jstype::VALUE;
}
} // namespace js

View file

@ -171,7 +171,7 @@ value::value(const std::string &s)
:value::root::type{[&s]
{
if(s.empty())
return JS::StringValue(JS_GetEmptyString(*rt));
return JS::StringValue(JS_GetEmptyString(*cx));
auto buf(native_external_copy(s));
const auto ret(JS_NewExternalString(*cx, buf.get(), s.size(), &native_external_delete));
@ -186,7 +186,7 @@ value::value(const char *const &s)
:value::root::type{[&s]
{
if(!s || !*s)
return JS::StringValue(JS_GetEmptyString(*rt));
return JS::StringValue(JS_GetEmptyString(*cx));
const auto len(strlen(s));
auto buf(native_external_copy(s, len));

View file

@ -64,11 +64,24 @@ struct vector<value>
struct handle
:JS::HandleValueArray
{
using JS::HandleValueArray::HandleValueArray;
handle(const JS::CallArgs &args): JS::HandleValueArray{args} {}
handle(): JS::HandleValueArray{JS::HandleValueArray::empty()} {}
handle()
:JS::HandleValueArray{JS::HandleValueArray::empty()}
{}
handle(const JS::CallArgs &args)
:JS::HandleValueArray{args}
{}
handle(const size_t &len, const JS::Value *const &elems)
:JS::HandleValueArray{JS::HandleValueArray::fromMarkedLocation(len, elems)}
{}
};
operator handle() const
{
return { length(), begin() };
}
/*
// Construct vector from initializer list of raw `JS::Value`
// ex: JS::Value a; vector foo {{ a, a, ... }};

3161
ircd/js.cc

File diff suppressed because it is too large Load diff

269
modules/kernel.cc Normal file
View file

@ -0,0 +1,269 @@
/*
* 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.
*/
#include <ircd/js/js.h>
struct assertions
{
assertions();
}
static assertions;
namespace ircd {
namespace js {
struct kernel
:trap
{
JSPrincipals *principals;
static const char *const source;
std::shared_ptr<task> process;
value on_call(object::handle, value::handle, const args &) override;
value on_set(object::handle, id::handle, value::handle) override;
value on_get(object::handle, id::handle, value::handle) override;
void on_add(object::handle, id::handle, value::handle) override;
bool on_del(object::handle, id::handle) override;
bool on_has(object::handle, id::handle) override;
void on_enu(object::handle) override;
void on_new(object::handle, object &, const args &) override;
void on_gc(JSObject *const &) override;
void main() noexcept;
kernel();
~kernel() noexcept;
}
static kernel;
} // namespace js
} // namespace ircd
///////////////////////////////////////////////////////////////////////////////
//
// Kernel source
//
const char *const
ircd::js::kernel::source
{R"(
'use strict';
import * as console from "server.console";
import * as listener from "server.listener";
var ircd =
{
};
ircd.opts =
{
};
ircd.opts.listener =
{
host: "127.0.0.1",
port: 8448,
backlog: 1024,
ssl_certificate_file: "/home/jason/newcert.pem",
ssl_private_key_file_pem: "/home/jason/privkey.pem",
ssl_certificate_chain_file: "/home/jason/newcert.pem",
ssl_tmp_dh_file: "/home/jason/dh512.pem",
};
let main = async function()
{
console.debug("IRCd.js Greetings from JavaScript");
listener.listen(ircd.opts.listener);
};
let fini = function()
{
console.info("IRCd.js finished");
}
main().then(fini);
)"};
///////////////////////////////////////////////////////////////////////////////
ircd::js::kernel::kernel()
:trap
{
"[global]",
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1) | JSCLASS_HAS_PRIVATE | JSCLASS_EMULATES_UNDEFINED,
}
,principals
{
nullptr
}
,process
{
std::make_shared<task>(source)
}
{
}
ircd::js::kernel::~kernel()
noexcept
{
}
void
ircd::js::kernel::main()
noexcept try
{
std::map<std::string, ircd::module> modules;
// These modules host databases and have to be loaded first.
modules.emplace("root.so"s, "root.so"s);
modules.emplace("client_account.so"s, "client_account.so"s);
modules.emplace("client_room.so"s, "client_room.so"s);
for(const auto &name : mods::available())
if(startswith(name, "client_"))
modules.emplace(name, name);
task::enter(process, [](auto &task)
{
task.main();
});
printf("main finished\n");
ctx::wait();
log.debug("Kernel finished");
}
catch(const ircd::ctx::interrupted &e)
{
log.debug("Kernel interrupted");
return;
}
catch(const std::exception &e)
{
log.critical("Kernel PANIC: %s", e.what());
std::terminate();
}
void
ircd::js::kernel::on_gc(JSObject *const &obj)
{
//trap::on_gc(obj);
}
void
ircd::js::kernel::on_new(object::handle ca,
object &obj,
const args &args)
{
trap::on_new(ca, obj, args);
}
void
ircd::js::kernel::on_enu(object::handle obj)
{
if(!JS_EnumerateStandardClasses(*cx, obj))
throw internal_error("Failed to enumerate standard classes");
}
bool
ircd::js::kernel::on_has(object::handle obj,
id::handle id)
{
return trap::on_has(obj, id);
}
bool
ircd::js::kernel::on_del(object::handle obj,
id::handle id)
{
return trap::on_del(obj, id);
}
void
ircd::js::kernel::on_add(object::handle obj,
id::handle id,
value::handle val)
{
trap::on_add(obj, id, val);
}
ircd::js::value
ircd::js::kernel::on_get(object::handle obj,
id::handle id,
value::handle val)
{
return trap::on_get(obj, id, val);
}
ircd::js::value
ircd::js::kernel::on_set(object::handle obj,
id::handle id,
value::handle val)
{
return trap::on_set(obj, id, val);
}
ircd::js::value
ircd::js::kernel::on_call(object::handle ca,
value::handle that,
const args &args)
{
return trap::on_call(ca, that, args);
}
// Unmangled alias to the main task.
extern "C"
void kmain()
{
ircd::js::kernel.main();
}
assertions::assertions()
{
using ircd::js::error;
if(!ircd::js::cx)
throw error("Kernel cannot find required JS context instance on this thread.");
}
const auto on_load([]
{
using namespace ircd::js;
});
const auto on_unload([]
{
using namespace ircd::js;
});
ircd::mapi::header IRCD_MODULE
{
"IRCd.js kernel - program which helps programs run",
on_load,
on_unload
};

173
modules/server/console.cc Normal file
View file

@ -0,0 +1,173 @@
/*
* 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.
*/
#include <ircd/js/js.h>
namespace ircd {
namespace js {
struct console
:trap
{
static const char *const source;
struct module module;
console();
}
static console;
console::console()
:trap
{
"__console",
JSCLASS_HAS_PRIVATE
}
,module
{
JS::CompileOptions(*cx),
locale::char16::conv(source),
this,
true
}
{
}
const char *const
console::source
{R"(
export function critical(msg) { __console.critical(msg); }
export function error(msg) { __console.error(msg); }
export function warn(msg) { __console.warn(msg); }
export function notice(msg) { __console.notice(msg); }
export function info(msg) { __console.info(msg); }
export function debug(msg) { __console.debug(msg); }
export function cout(msg) { __console.cout(msg); }
export function log(msg) { __console.info(msg); }
)"};
struct console_critical
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log(ircd::log::CRITICAL, "%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_critical{console, "critical"};
struct console_error
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log(ircd::log::ERROR, "%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_error{console, "error"};
struct console_warn
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log(ircd::log::WARNING, "%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_warn{console, "warn"};
struct console_notice
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log(ircd::log::NOTICE, "%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_notice{console, "notice"};
struct console_info
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log(ircd::log::INFO, "%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_info{console, "info"};
struct console_debug
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
ircd::js::log.debug("%s", std::string(args[0]));
return {};
}
using trap::function::function;
}
static console_debug{console, "debug"};
struct console_cout
:trap::function
{
value on_call(object::handle obj, value::handle _this, const args &args) override
{
std::cout << string(args[0]) << std::endl;
return {};
}
using trap::function::function;
}
static console_cout{console, "cout"};
} // namespace js
} // namespace ircd
extern "C" ircd::js::module *
IRCD_JS_MODULE
{
&ircd::js::console.module
};
ircd::mapi::header
IRCD_MODULE
{
"Provides simple I/O for debugging similar to that found in web browsers."
};

View file

@ -19,72 +19,87 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#define HAVE_IRCD_JS_GENERATOR_H
#include <ircd/listen.h>
#include <ircd/js/js.h>
namespace ircd {
namespace js {
struct generator
struct listener
:js::trap
{
object state;
object last;
struct listen;
bool done() const;
template<class... args> value next(args&&...);
template<class... args> value _throw(args&&...);
template<class... args> value _return(args&&...);
static const char *const source;
struct module module;
generator(object::handle state);
generator(object state);
generator() = default;
listener();
}
static listener;
struct listener::listen
:trap::function
{
ircd::listener *l;
value on_call(object::handle obj, value::handle _this, const args &args) override
{
l = new ircd::listener(std::string(args[0]));
return {};
}
using trap::function::function;
}
static listener_listen
{
listener, "listen"
};
inline
generator::generator(object state)
:state{state}
{
}
inline
generator::generator(object::handle state)
:state{state}
{
}
template<class... args>
value
generator::_return(args&&... a)
{
function func(get(state, "return"));
last = func(state, std::forward<args>(a)...);
return has(last, "value")? get(last, "value") : value{};
}
template<class... args>
value
generator::_throw(args&&... a)
{
function func(get(state, "throw"));
last = func(state, std::forward<args>(a)...);
return has(last, "value")? get(last, "value") : value{};
}
template<class... args>
value
generator::next(args&&... a)
{
function func(get(state, "next"));
last = func(state, std::forward<args>(a)...);
return has(last, "value")? get(last, "value") : value{};
}
inline bool
generator::done()
const
{
return bool(get(last, "done"));
}
} // namespace js
} // namespace ircd
///////////////////////////////////////////////////////////////////////////////
//
// Listener source
//
const char *const
ircd::js::listener::source
{R"(
import * as console from "server.console";
export function listen(opts)
{
__listener.listen(JSON.stringify(opts));
}
)"};
///////////////////////////////////////////////////////////////////////////////
ircd::js::listener::listener()
:js::trap
{
"__listener",
}
,module
{
JS::CompileOptions(*cx),
locale::char16::conv(source),
this,
true
}
{
}
extern "C" ircd::js::module *
IRCD_JS_MODULE
{
&ircd::js::listener.module
};
ircd::mapi::header
IRCD_MODULE
{
"Network listener socket support for servers"
};

View file

@ -46,8 +46,8 @@ USERDIR=$PWD # Save current dir and return to it later
run git submodule update --init --remote gecko-dev
run cd gecko-dev
#run git fetch --depth=1 origin esr45
#run git checkout FETCH_HEAD
#run git fetch --depth=1 origin $BRANCH
run git checkout $BRANCH
run cd js/src
run autoconf2.13
@ -60,12 +60,12 @@ run cd build_OPT.OBJ
# may actually be the best behavior in production but right now this test
# prevents rebuilds
if test $ALREADY_EXISTS -eq 0; then
run ../configure $CONFIG_OPTIONS
run ../configure $CONFIG_OPTIONS JS_STANDALONE=1
fi
#run ../configure --enable-debug
# run ../configure --disable-shared-js --enable-debug # --enable-replace-malloc
run make
run make -j8
run cd $USERDIR # Return to user's original directory