[gjs: 1/11] engine: Use a "source hook" and keep sources in memory



commit b032fda4a4c33b07ff9f9f25c1bbc93adc5be316
Author: Philip Chimento <philip chimento gmail com>
Date:   Mon Oct 8 14:42:08 2018 -0700

    engine: Use a "source hook" and keep sources in memory
    
    Previously we always set the "source is lazy" flag on all code that we
    evaluated. This was incorrect, since that meant that SpiderMonkey would
    not keep the source code in memory, so it would not do on-the-fly
    bytecode generation, and the debugger would not be able to examine
    sources.
    
    If you use lazy sources, you are supposed to define a "source hook"
    object that will return source code based on the source's URI.
    
    This commit removes the lazy bit from all sources, except for compartment
    bootstrap code; those sources already come from a GResource, so they are
    already stored in memory and it's easy to write a source hook to retrieve
    them.
    
    That could be expanded to cover modules imported from GResource sources,
    but that seems a bit overcomplicated.

 gjs/engine.cpp     | 43 +++++++++++++++++++++++++++++++++++++++++++
 gjs/engine.h       |  3 +++
 gjs/global.cpp     | 22 +++++++++-------------
 gjs/jsapi-util.cpp |  3 +--
 gjs/module.cpp     |  3 +--
 5 files changed, 57 insertions(+), 17 deletions(-)
---
diff --git a/gjs/engine.cpp b/gjs/engine.cpp
index 1b9b1de0..4f50e417 100644
--- a/gjs/engine.cpp
+++ b/gjs/engine.cpp
@@ -23,6 +23,8 @@
 
 #include <config.h>
 
+#include <gio/gio.h>
+
 #include "jsapi-wrapper.h"
 #include <js/Initialization.h>
 
@@ -215,6 +217,39 @@ static void on_promise_unhandled_rejection(
                                                       std::move(stack));
 }
 
+bool gjs_load_internal_source(JSContext* cx, const char* filename,
+                              JS::UniqueTwoByteChars* src, size_t* length) {
+    GError* error = nullptr;
+    const char* path = filename + 11;  // len("resource://")
+    GjsAutoPointer<GBytes, GBytes, g_bytes_unref> script_bytes;
+    script_bytes =
+        g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+    if (!script_bytes)
+        return gjs_throw_gerror_message(cx, error);
+
+    size_t script_len;
+    const void* script_data = g_bytes_get_data(script_bytes, &script_len);
+    JS::ConstUTF8CharsZ script(static_cast<const char*>(script_data),
+                               script_len);
+    JS::TwoByteCharsZ chs = JS::UTF8CharsToNewTwoByteCharsZ(cx, script, length);
+    if (!chs)
+        return false;
+
+    src->reset(chs.get());
+    return true;
+}
+
+class GjsSourceHook : public js::SourceHook {
+    bool load(JSContext* cx, const char* filename, char16_t** src,
+              size_t* length) {
+        JS::UniqueTwoByteChars chars;
+        if (!gjs_load_internal_source(cx, filename, &chars, length))
+            return false;
+        *src = chars.release();  // caller owns, per documentation of SourceHook
+        return true;
+    }
+};
+
 #ifdef G_OS_WIN32
 HMODULE gjs_dll;
 static bool gjs_is_inited = false;
@@ -303,6 +338,14 @@ gjs_create_js_context(GjsContext *js_context)
     JS::SetPromiseRejectionTrackerCallback(cx, on_promise_unhandled_rejection,
                                            js_context);
 
+    /* We use this to handle "lazy sources" that SpiderMonkey doesn't need to
+     * keep in memory. Most sources should be kept in memory, but we can skip
+     * doing that for the compartment bootstrap code, as it is already in memory
+     * in the form of a GResource. Instead we use the "source hook" to
+     * retrieve it. */
+    auto hook = mozilla::MakeUnique<GjsSourceHook>();
+    js::SetSourceHook(cx, std::move(hook));
+
     /* setExtraWarnings: Be extra strict about code that might hide a bug */
     if (!g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) {
         gjs_debug(GJS_DEBUG_CONTEXT, "Enabling extra warnings");
diff --git a/gjs/engine.h b/gjs/engine.h
index 46bce817..fab03d3a 100644
--- a/gjs/engine.h
+++ b/gjs/engine.h
@@ -29,4 +29,7 @@
 
 JSContext *gjs_create_js_context(GjsContext *js_context);
 
+bool gjs_load_internal_source(JSContext* cx, const char* filename,
+                              JS::UniqueTwoByteChars* src, size_t* length);
+
 #endif  /* GJS_ENGINE_H */
diff --git a/gjs/global.cpp b/gjs/global.cpp
index 0099fcbc..7bb8c0c6 100644
--- a/gjs/global.cpp
+++ b/gjs/global.cpp
@@ -25,6 +25,7 @@
 
 #include <gio/gio.h>
 
+#include "gjs/engine.h"
 #include "global.h"
 #include "importer.h"
 #include "jsapi-util.h"
@@ -36,28 +37,23 @@ run_bootstrap(JSContext       *cx,
               const char      *bootstrap_script,
               JS::HandleObject global)
 {
-    GjsAutoChar path = g_strdup_printf("/org/gnome/gjs/modules/_bootstrap/%s.js",
-                                       bootstrap_script);
-    GError *error = nullptr;
-    GjsAutoPointer<GBytes, GBytes, g_bytes_unref> script_bytes;
-    script_bytes =
-        g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
-    if (!script_bytes)
-        return gjs_throw_gerror_message(cx, error);
+    GjsAutoChar uri = g_strdup_printf(
+        "resource:///org/gnome/gjs/modules/_bootstrap/%s.js", bootstrap_script);
 
     JSAutoCompartment ac(cx, global);
 
-    GjsAutoChar uri = g_strconcat("resource://", path.get(), nullptr);
     JS::CompileOptions options(cx);
     options.setUTF8(true)
            .setFileAndLine(uri, 1)
            .setSourceIsLazy(true);
 
-    JS::RootedScript compiled_script(cx);
+    JS::UniqueTwoByteChars script;
     size_t script_len;
-    auto script =
-        static_cast<const char*>(g_bytes_get_data(script_bytes, &script_len));
-    if (!JS::Compile(cx, options, script, script_len, &compiled_script))
+    if (!gjs_load_internal_source(cx, uri.get(), &script, &script_len))
+        return false;
+
+    JS::RootedScript compiled_script(cx);
+    if (!JS::Compile(cx, options, script.get(), script_len, &compiled_script))
         return false;
 
     JS::RootedValue ignored(cx);
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 7b6d872e..31a11841 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -808,8 +808,7 @@ gjs_eval_with_scope(JSContext             *context,
         eval_obj = JS_NewPlainObject(context);
 
     JS::CompileOptions options(context);
-    options.setFileAndLine(filename, start_line_number)
-           .setSourceIsLazy(true);
+    options.setFileAndLine(filename, start_line_number);
 
     std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
     std::u16string utf16_string = convert.from_bytes(script);
diff --git a/gjs/module.cpp b/gjs/module.cpp
index a5330ee7..7a50ece5 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -93,8 +93,7 @@ class GjsModule {
                     int              line_number)
     {
         JS::CompileOptions options(cx);
-        options.setFileAndLine(filename, line_number)
-               .setSourceIsLazy(true);
+        options.setFileAndLine(filename, line_number);
 
         std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
         std::u16string utf16_string = convert.from_bytes(script);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]