Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
4 : :
5 : : #include <config.h>
6 : :
7 : : #include <stdint.h>
8 : :
9 : : #ifdef _WIN32
10 : : # include <windows.h>
11 : : #endif
12 : :
13 : : #include <utility> // for move
14 : :
15 : : #include <gio/gio.h>
16 : : #include <glib.h>
17 : :
18 : : #include <js/Context.h>
19 : : #include <js/ContextOptions.h>
20 : : #include <js/GCAPI.h> // for JS_SetGCParameter, JS_AddFin...
21 : : #include <js/Initialization.h> // for JS_Init, JS_ShutDown
22 : : #include <js/Principals.h>
23 : : #include <js/Promise.h>
24 : : #include <js/RootingAPI.h>
25 : : #include <js/Stack.h> // for JS_SetNativeStackQuota
26 : : #include <js/StructuredClone.h> // for JS_WriteUint32Pair
27 : : #include <js/TypeDecls.h>
28 : : #include <js/Utility.h> // for UniqueChars
29 : : #include <js/Warnings.h>
30 : : #include <js/experimental/SourceHook.h>
31 : : #include <jsapi.h> // for JS_SetGlobalJitCompilerOption
32 : : #include <jsfriendapi.h> // for SetDOMCallbacks, DOMCallbacks
33 : : #include <mozilla/UniquePtr.h>
34 : :
35 : : #ifndef G_DISABLE_ASSERT
36 : : # include <mozilla/Atomics.h> // for Atomic::operator==
37 : : #endif
38 : :
39 : : #include "gi/gerror.h"
40 : : #include "gjs/context-private.h"
41 : : #include "gjs/engine.h"
42 : : #include "gjs/gerror-result.h"
43 : : #include "gjs/jsapi-util.h"
44 : : #include "gjs/profiler-private.h"
45 : : #include "util/log.h"
46 : :
47 : 2372 : static void gjs_finalize_callback(JS::GCContext*, JSFinalizeStatus status,
48 : : void* data) {
49 : 2372 : auto* gjs = static_cast<GjsContextPrivate*>(data);
50 [ - + ]: 2372 : if (gjs->profiler())
51 : 0 : _gjs_profiler_set_finalize_status(gjs->profiler(), status);
52 : 2372 : }
53 : :
54 : 51 : static void on_promise_unhandled_rejection(
55 : : JSContext* cx, bool mutedErrors [[maybe_unused]], JS::HandleObject promise,
56 : : JS::PromiseRejectionHandlingState state, void* data) {
57 : 51 : auto* gjs = static_cast<GjsContextPrivate*>(data);
58 : 51 : uint64_t id = JS::GetPromiseID(promise);
59 : :
60 [ + + ]: 51 : if (state == JS::PromiseRejectionHandlingState::Handled) {
61 : : // This happens when catching an exception from an await expression.
62 : 25 : gjs->unregister_unhandled_promise_rejection(id);
63 : 25 : return;
64 : : }
65 : :
66 : 26 : JS::RootedObject allocation_site(cx, JS::GetPromiseAllocationSite(promise));
67 : 26 : JS::UniqueChars stack = format_saved_frame(cx, allocation_site);
68 : 52 : gjs->register_unhandled_promise_rejection(id, std::move(stack));
69 : 26 : }
70 : :
71 : 5 : static void on_cleanup_finalization_registry(JSFunction* cleanup_task,
72 : : JSObject* incumbent_global
73 : : [[maybe_unused]],
74 : : void* data) {
75 : 5 : auto* gjs = static_cast<GjsContextPrivate*>(data);
76 [ - + ]: 5 : if (!gjs->queue_finalization_registry_cleanup(cleanup_task))
77 : 0 : g_critical("Out of memory queueing FinalizationRegistry cleanup task");
78 : 5 : }
79 : :
80 : 614 : bool gjs_load_internal_source(JSContext* cx, const char* filename, char** src,
81 : : size_t* length) {
82 : 614 : Gjs::AutoError error;
83 : 614 : const char* path = filename + 11; // len("resource://")
84 : : GBytes* script_bytes =
85 : 614 : g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
86 [ - + ]: 614 : if (!script_bytes)
87 : 0 : return gjs_throw_gerror_message(cx, error);
88 : :
89 : 614 : *src = static_cast<char*>(g_bytes_unref_to_data(script_bytes, length));
90 : 614 : return true;
91 : 614 : }
92 : :
93 : : class GjsSourceHook : public js::SourceHook {
94 : 0 : bool load(JSContext* cx, const char* filename,
95 : : char16_t** two_byte_source [[maybe_unused]], char** utf8_source,
96 : : size_t* length) {
97 : : // caller owns the source, per documentation of SourceHook
98 : 0 : return gjs_load_internal_source(cx, filename, utf8_source, length);
99 : : }
100 : : };
101 : :
102 : : #ifdef G_OS_WIN32
103 : : HMODULE gjs_dll;
104 : : static bool gjs_is_inited = false;
105 : :
106 : : BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
107 : : switch (fdwReason) {
108 : : case DLL_PROCESS_ATTACH: {
109 : : gjs_dll = hinstDLL;
110 : : const char* reason = JS_InitWithFailureDiagnostic();
111 : : if (reason)
112 : : g_error("Could not initialize JavaScript: %s", reason);
113 : : gjs_is_inited = true;
114 : : } break;
115 : :
116 : : case DLL_THREAD_DETACH:
117 : : JS_ShutDown();
118 : : break;
119 : :
120 : : default: {
121 : : }
122 : : }
123 : :
124 : : return TRUE;
125 : : }
126 : :
127 : : #else
128 : : class GjsInit {
129 : : public:
130 : 129 : GjsInit() {
131 : 129 : const char* reason = JS_InitWithFailureDiagnostic();
132 [ - + ]: 129 : if (reason)
133 : 0 : g_error("Could not initialize JavaScript: %s", reason);
134 : 129 : }
135 : :
136 : 129 : ~GjsInit() {
137 : 129 : JS_ShutDown();
138 : 129 : }
139 : :
140 : 255 : explicit operator bool() const { return true; }
141 : : };
142 : :
143 : : static GjsInit gjs_is_inited;
144 : : #endif
145 : :
146 : : // JSPrincipals (basically a weird name for security callbacks) which are in
147 : : // effect in the module loader's realm (GjsInternalGlobal). This prevents module
148 : : // loader stack frames from showing up in public stack traces.
149 : : class ModuleLoaderPrincipals final : public JSPrincipals {
150 : : static constexpr uint32_t STRUCTURED_CLONE_TAG = JS_SCTAG_USER_MIN;
151 : :
152 : 0 : bool write(JSContext* cx [[maybe_unused]],
153 : : JSStructuredCloneWriter* writer) override {
154 : : g_assert_not_reached();
155 : : return JS_WriteUint32Pair(writer, STRUCTURED_CLONE_TAG, 1);
156 : : }
157 : :
158 : 0 : bool isSystemOrAddonPrincipal() override { return true; }
159 : :
160 : : public:
161 : 129808 : static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
162 [ + - + + ]: 129808 : if (first != &the_principals && second == &the_principals)
163 : 114763 : return false;
164 : 15045 : return true;
165 : : }
166 : :
167 : 253 : static void destroy(JSPrincipals* principals [[maybe_unused]]) {
168 : 253 : g_assert(principals == &the_principals &&
169 : : "Should not create other instances of ModuleLoaderPrinciples");
170 : 253 : g_assert(principals->refcount == 0 &&
171 : : "Mismatched JS_HoldPrincipals/JS_DropPrincipals");
172 : 253 : }
173 : :
174 : : // Singleton
175 : : static ModuleLoaderPrincipals the_principals;
176 : : };
177 : :
178 : : ModuleLoaderPrincipals ModuleLoaderPrincipals::the_principals{};
179 : :
180 : 255 : JSPrincipals* get_internal_principals() {
181 : 255 : return &ModuleLoaderPrincipals::the_principals;
182 : : }
183 : :
184 : : static const JSSecurityCallbacks security_callbacks = {
185 : : /* contentSecurityPolicyAllows = */ nullptr,
186 : : /* codeForEvalGets = */ nullptr,
187 : : &ModuleLoaderPrincipals::subsumes,
188 : : };
189 : :
190 : 4 : static bool instance_class_is_error(const JSClass* klass) {
191 : 4 : return klass == &ErrorBase::klass;
192 : : }
193 : :
194 : : static const js::DOMCallbacks dom_callbacks = {
195 : : /* instanceClassHasProtoAtDepth = */ nullptr,
196 : : &instance_class_is_error,
197 : : };
198 : :
199 : 255 : JSContext* gjs_create_js_context(GjsContextPrivate* uninitialized_gjs) {
200 : 255 : g_assert(gjs_is_inited);
201 : 255 : JSContext* cx = JS_NewContext(/* max bytes = */ 32 * 1024 * 1024);
202 [ - + ]: 255 : if (!cx)
203 : 0 : return nullptr;
204 : :
205 [ - + ]: 255 : if (!JS::InitSelfHostedCode(cx)) {
206 : 0 : JS_DestroyContext(cx);
207 : 0 : return nullptr;
208 : : }
209 : :
210 : : // For additional context on these options, see
211 : : // https://searchfox.org/mozilla-esr91/rev/c49725508e97c1e2e2bb3bf9ed0ba14b2016abac/js/public/GCAPI.h#53
212 : 255 : JS_SetNativeStackQuota(cx, 1024 * 1024);
213 : 255 : JS_SetGCParameter(cx, JSGC_MAX_BYTES, -1);
214 : 255 : JS_SetGCParameter(cx, JSGC_INCREMENTAL_GC_ENABLED, 1);
215 : 255 : JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET_MS, 10);
216 : :
217 : : // set ourselves as the private data
218 : 255 : JS_SetContextPrivate(cx, uninitialized_gjs);
219 : :
220 : 255 : JS_SetSecurityCallbacks(cx, &security_callbacks);
221 : 255 : JS_InitDestroyPrincipalsCallback(cx, &ModuleLoaderPrincipals::destroy);
222 : 255 : JS_AddFinalizeCallback(cx, gjs_finalize_callback, uninitialized_gjs);
223 : 255 : JS::SetWarningReporter(cx, gjs_warning_reporter);
224 : 255 : JS::SetJobQueue(cx, dynamic_cast<JS::JobQueue*>(uninitialized_gjs));
225 : 255 : JS::SetPromiseRejectionTrackerCallback(cx, on_promise_unhandled_rejection,
226 : : uninitialized_gjs);
227 : 255 : JS::SetHostCleanupFinalizationRegistryCallback(
228 : : cx, on_cleanup_finalization_registry, uninitialized_gjs);
229 : 255 : js::SetDOMCallbacks(cx, &dom_callbacks);
230 : :
231 : : // We use this to handle "lazy sources" that SpiderMonkey doesn't need to
232 : : // keep in memory. Most sources should be kept in memory, but we can skip
233 : : // doing that for the realm bootstrap code, as it is already in memory in
234 : : // the form of a GResource. Instead we use the "source hook" to retrieve it.
235 : 255 : auto hook = mozilla::MakeUnique<GjsSourceHook>();
236 : 255 : js::SetSourceHook(cx, std::move(hook));
237 : :
238 [ - + ]: 255 : if (g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) {
239 : 0 : g_warning(
240 : : "GJS_DISABLE_EXTRA_WARNINGS has been removed, GJS no longer logs "
241 : : "extra warnings.");
242 : : }
243 : :
244 : 255 : bool enable_jit = !(g_getenv("GJS_DISABLE_JIT"));
245 [ + - ]: 255 : if (enable_jit) {
246 : 255 : gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT");
247 : : }
248 : 255 : JS::ContextOptionsRef(cx).setAsmJS(enable_jit);
249 : :
250 [ + - ]: 255 : uint32_t value = enable_jit ? 1 : 0;
251 : :
252 : 255 : JS_SetGlobalJitCompilerOption(
253 : : cx, JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE, value);
254 : 255 : JS_SetGlobalJitCompilerOption(
255 : : cx, JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE, value);
256 : 255 : JS_SetGlobalJitCompilerOption(
257 : : cx, JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
258 : : value);
259 : :
260 : 255 : return cx;
261 : 255 : }
|