2008-07-16 05:32:24 -04:00
|
|
|
/** SpiderMonkey support for both user scripts and web scripts.
|
|
|
|
* @file */
|
2008-06-16 17:25:59 -04:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
2008-07-16 05:32:24 -04:00
|
|
|
#include "ecmascript/spidermonkey-shared.h"
|
2008-09-07 12:39:26 -04:00
|
|
|
#include "ecmascript/spidermonkey.h"
|
|
|
|
#include "scripting/smjs/core.h"
|
2008-06-16 17:25:59 -04:00
|
|
|
|
2008-07-16 07:23:19 -04:00
|
|
|
/** A shared runtime used for both user scripts (scripting/smjs/) and
|
|
|
|
* scripts on web pages (ecmascript/spidermonkey/).
|
|
|
|
*
|
|
|
|
* SpiderMonkey has bugs that corrupt memory when multiple JSRuntimes
|
|
|
|
* are used: https://bugzilla.mozilla.org/show_bug.cgi?id=378918 and
|
|
|
|
* perhaps others. */
|
|
|
|
JSRuntime *spidermonkey_runtime;
|
|
|
|
|
2008-07-07 08:57:47 -04:00
|
|
|
/** A JSContext that can be used in JS_SetPrivate and JS_GetPrivate
|
|
|
|
* when no better one is available. This context has no global
|
|
|
|
* object, so scripts cannot be evaluated in it.
|
|
|
|
*
|
|
|
|
* XXX: This also works around a crash on exit. SMJS will crash on
|
|
|
|
* JS_DestroyRuntime if the given runtime has never had any context
|
|
|
|
* created, which will be the case if one closes ELinks without having
|
|
|
|
* loaded any documents. */
|
|
|
|
JSContext *spidermonkey_empty_context;
|
|
|
|
|
2008-07-16 07:23:19 -04:00
|
|
|
/** A reference count for ::spidermonkey_runtime so that modules using
|
|
|
|
* it can be initialized and shut down in arbitrary order. */
|
|
|
|
static int spidermonkey_runtime_refcount;
|
|
|
|
|
2008-09-07 12:39:26 -04:00
|
|
|
static void
|
|
|
|
error_reporter(JSContext *ctx, const char *message, JSErrorReport *report)
|
|
|
|
{
|
|
|
|
/* We have three types of JSContexts.
|
|
|
|
* - spidermonkey_empty_context never has anything defined or
|
|
|
|
* evaluated in it, so this error_reporter() should not be
|
|
|
|
* called for it.
|
|
|
|
* - smjs_ctx for user scripts.
|
|
|
|
* - many JSContexts for web scripts.
|
|
|
|
* Check which one ctx is and call the appropriate function.
|
|
|
|
*
|
|
|
|
* Instead of the scheme used here, we could:
|
|
|
|
* (a) make the private pointer of every context point to a
|
|
|
|
* structure of known type and put a function pointer or
|
|
|
|
* enum in that structure, or
|
|
|
|
* (b) assume that JS_GetContextPrivate(smjs_ctx) == NULL. */
|
|
|
|
|
|
|
|
assert(ctx != spidermonkey_empty_context);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
|
|
|
if (ctx == smjs_ctx) {
|
|
|
|
smjs_error_reporter(ctx, message, report);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ECMASCRIPT_SMJS
|
|
|
|
spidermonkey_error_reporter(ctx, message, report);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-07-07 08:57:47 -04:00
|
|
|
/** Initialize ::spidermonkey_runtime and ::spidermonkey_empty_context.
|
|
|
|
* If already initialized, just increment the reference count.
|
|
|
|
*
|
2008-07-16 07:23:19 -04:00
|
|
|
* @return 1 if successful or 0 on error. If this succeeds, the
|
|
|
|
* caller must eventually call spidermonkey_runtime_release(). */
|
|
|
|
int
|
|
|
|
spidermonkey_runtime_addref(void)
|
|
|
|
{
|
2008-07-07 08:57:47 -04:00
|
|
|
if (spidermonkey_runtime_refcount == 0) {
|
|
|
|
assert(spidermonkey_runtime == NULL);
|
|
|
|
assert(spidermonkey_empty_context == NULL);
|
2008-07-16 07:23:19 -04:00
|
|
|
if_assert_failed return 0;
|
2008-07-16 07:50:41 -04:00
|
|
|
|
|
|
|
spidermonkey_runtime = JS_NewRuntime(4L * 1024L * 1024L);
|
2008-07-16 07:23:19 -04:00
|
|
|
if (!spidermonkey_runtime) return 0;
|
2008-07-07 08:57:47 -04:00
|
|
|
|
|
|
|
spidermonkey_empty_context = JS_NewContext(spidermonkey_runtime,
|
|
|
|
0);
|
|
|
|
if (!spidermonkey_empty_context) {
|
|
|
|
/* Perhaps JS_DestroyRuntime will now crash
|
|
|
|
* because no context was created, but there's
|
|
|
|
* not much else to do. */
|
|
|
|
JS_DestroyRuntime(spidermonkey_runtime);
|
|
|
|
spidermonkey_runtime = NULL;
|
|
|
|
JS_ShutDown();
|
2008-09-07 11:04:18 -04:00
|
|
|
return 0;
|
2008-07-07 08:57:47 -04:00
|
|
|
}
|
2008-09-07 12:39:26 -04:00
|
|
|
|
|
|
|
/* Although JS_SetErrorReporter gets the JSContext as
|
|
|
|
* a parameter, it affects the whole JSRuntime. */
|
|
|
|
JS_SetErrorReporter(spidermonkey_empty_context,
|
|
|
|
error_reporter);
|
2008-07-16 07:23:19 -04:00
|
|
|
}
|
2008-07-16 07:50:41 -04:00
|
|
|
|
2008-07-07 08:57:47 -04:00
|
|
|
assert(spidermonkey_runtime);
|
|
|
|
assert(spidermonkey_empty_context);
|
2008-07-16 07:23:19 -04:00
|
|
|
spidermonkey_runtime_refcount++;
|
|
|
|
assert(spidermonkey_runtime_refcount > 0);
|
|
|
|
if_assert_failed { spidermonkey_runtime_refcount--; return 0; }
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Release a reference to ::spidermonkey_runtime, and destroy it if
|
|
|
|
* that was the last reference. If spidermonkey_runtime_addref()
|
|
|
|
* failed, then this must not be called. */
|
|
|
|
void
|
|
|
|
spidermonkey_runtime_release(void)
|
|
|
|
{
|
|
|
|
assert(spidermonkey_runtime_refcount > 0);
|
|
|
|
assert(spidermonkey_runtime);
|
2008-07-07 08:57:47 -04:00
|
|
|
assert(spidermonkey_empty_context);
|
2008-07-16 07:23:19 -04:00
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
--spidermonkey_runtime_refcount;
|
|
|
|
if (spidermonkey_runtime_refcount == 0) {
|
2008-07-07 08:57:47 -04:00
|
|
|
JS_DestroyContext(spidermonkey_empty_context);
|
|
|
|
spidermonkey_empty_context = NULL;
|
2008-07-16 07:23:19 -04:00
|
|
|
JS_DestroyRuntime(spidermonkey_runtime);
|
|
|
|
spidermonkey_runtime = NULL;
|
2008-07-16 07:50:41 -04:00
|
|
|
JS_ShutDown();
|
2008-07-16 07:23:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-16 17:25:59 -04:00
|
|
|
/** An ELinks-specific replacement for JS_DefineFunctions().
|
|
|
|
*
|
|
|
|
* @relates spidermonkeyFunctionSpec */
|
|
|
|
JSBool
|
|
|
|
spidermonkey_DefineFunctions(JSContext *cx, JSObject *obj,
|
|
|
|
const spidermonkeyFunctionSpec *fs)
|
|
|
|
{
|
|
|
|
for (; fs->name; fs++) {
|
|
|
|
if (!JS_DefineFunction(cx, obj, fs->name, fs->call,
|
|
|
|
fs->nargs, 0))
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** An ELinks-specific replacement for JS_InitClass().
|
|
|
|
*
|
|
|
|
* @relates spidermonkeyFunctionSpec */
|
|
|
|
JSObject *
|
|
|
|
spidermonkey_InitClass(JSContext *cx, JSObject *obj,
|
|
|
|
JSObject *parent_proto, JSClass *clasp,
|
|
|
|
JSNative constructor, uintN nargs,
|
|
|
|
JSPropertySpec *ps,
|
|
|
|
const spidermonkeyFunctionSpec *fs,
|
|
|
|
JSPropertySpec *static_ps,
|
|
|
|
const spidermonkeyFunctionSpec *static_fs)
|
|
|
|
{
|
|
|
|
JSObject *proto = JS_InitClass(cx, obj, parent_proto, clasp,
|
|
|
|
constructor, nargs,
|
|
|
|
ps, NULL, static_ps, NULL);
|
|
|
|
|
|
|
|
if (proto == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (fs) {
|
|
|
|
if (!spidermonkey_DefineFunctions(cx, proto, fs))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (static_fs) {
|
|
|
|
JSObject *cons_obj = JS_GetConstructor(cx, proto);
|
|
|
|
|
|
|
|
if (cons_obj == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (!spidermonkey_DefineFunctions(cx, cons_obj, static_fs))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return proto;
|
|
|
|
}
|