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: 2014 Colin Walters <walters@verbum.org>
4 : :
5 : : #ifndef GJS_CONTEXT_PRIVATE_H_
6 : : #define GJS_CONTEXT_PRIVATE_H_
7 : :
8 : : #include <config.h>
9 : :
10 : : #include <stddef.h> // for size_t
11 : : #include <stdint.h>
12 : :
13 : : #include <atomic>
14 : : #include <string>
15 : : #include <thread>
16 : : #include <unordered_map>
17 : : #include <utility> // for pair
18 : : #include <vector>
19 : :
20 : : #include <gio/gio.h> // for GMemoryMonitor
21 : : #include <glib-object.h>
22 : : #include <glib.h>
23 : :
24 : : #include <js/AllocPolicy.h>
25 : : #include <js/Context.h>
26 : : #include <js/GCAPI.h>
27 : : #include <js/GCHashTable.h>
28 : : #include <js/GCVector.h>
29 : : #include <js/HashTable.h> // for DefaultHasher
30 : : #include <js/Promise.h>
31 : : #include <js/Realm.h>
32 : : #include <js/RootingAPI.h>
33 : : #include <js/TypeDecls.h>
34 : : #include <js/UniquePtr.h>
35 : : #include <js/Utility.h> // for UniqueChars, FreePolicy
36 : : #include <js/ValueArray.h>
37 : : #include <jsfriendapi.h> // for ScriptEnvironmentPreparer
38 : :
39 : : #include "gi/closure.h"
40 : : #include "gjs/auto.h"
41 : : #include "gjs/context.h"
42 : : #include "gjs/gerror-result.h"
43 : : #include "gjs/jsapi-util-root.h"
44 : : #include "gjs/macros.h"
45 : : #include "gjs/mainloop.h"
46 : : #include "gjs/profiler.h"
47 : : #include "gjs/promise.h"
48 : :
49 : : class GjsAtoms;
50 : : class JSTracer;
51 : :
52 : : using JobQueueStorage =
53 : : JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>;
54 : : using ObjectInitList =
55 : : JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>;
56 : : using FundamentalTable =
57 : : JS::GCHashMap<void*, Gjs::WeakPtr<JSObject*>, js::DefaultHasher<void*>,
58 : : js::SystemAllocPolicy>;
59 : : using GTypeTable =
60 : : JS::GCHashMap<GType, Gjs::WeakPtr<JSObject*>, js::DefaultHasher<GType>,
61 : : js::SystemAllocPolicy>;
62 : : using FunctionVector = JS::GCVector<JSFunction*, 0, js::SystemAllocPolicy>;
63 : :
64 : : class GjsContextPrivate : public JS::JobQueue {
65 : : public:
66 : : using DestroyNotify = void (*)(JSContext*, void* data);
67 : :
68 : : private:
69 : : GjsContext* m_public_context;
70 : : JSContext* m_cx;
71 : : JS::Heap<JSObject*> m_main_loop_hook;
72 : : JS::Heap<JSObject*> m_global;
73 : : JS::Heap<JSObject*> m_internal_global;
74 : : std::thread::id m_owner_thread;
75 : :
76 : : char* m_program_name;
77 : : char* m_program_path;
78 : :
79 : : char** m_search_path;
80 : :
81 : : char* m_repl_history_path;
82 : :
83 : : unsigned m_auto_gc_id;
84 : :
85 : : GjsAtoms* m_atoms;
86 : :
87 : : std::vector<std::string> m_args;
88 : :
89 : : JobQueueStorage m_job_queue;
90 : : Gjs::PromiseJobDispatcher m_dispatcher;
91 : : Gjs::MainLoop m_main_loop;
92 : : Gjs::AutoUnref<GMemoryMonitor> m_memory_monitor;
93 : :
94 : : std::vector<std::pair<DestroyNotify, void*>> m_destroy_notifications;
95 : : std::vector<Gjs::Closure::Ptr> m_async_closures;
96 : : std::unordered_map<uint64_t, JS::UniqueChars> m_unhandled_rejection_stacks;
97 : : FunctionVector m_cleanup_tasks;
98 : :
99 : : GjsProfiler* m_profiler;
100 : :
101 : : /* Environment preparer needed for debugger, taken from SpiderMonkey's
102 : : * JS shell */
103 : : struct EnvironmentPreparer final : protected js::ScriptEnvironmentPreparer {
104 : : JSContext* m_cx;
105 : :
106 : 257 : explicit EnvironmentPreparer(JSContext* cx) : m_cx(cx) {
107 : 257 : js::SetScriptEnvironmentPreparer(m_cx, this);
108 : 257 : }
109 : :
110 : : void invoke(JS::HandleObject scope, Closure& closure) override;
111 : : };
112 : : EnvironmentPreparer m_environment_preparer;
113 : :
114 : : // Weak pointer mapping from fundamental native pointer to JSObject
115 : : JS::WeakCache<FundamentalTable>* m_fundamental_table;
116 : : JS::WeakCache<GTypeTable>* m_gtype_table;
117 : :
118 : : // List that holds JSObject GObject wrappers for JS-created classes, from
119 : : // the time of their creation until their GObject instance init function is
120 : : // called
121 : : ObjectInitList m_object_init_list;
122 : :
123 : : uint8_t m_exit_code;
124 : :
125 : : /* flags */
126 : : std::atomic_bool m_destroying = ATOMIC_VAR_INIT(false);
127 : : bool m_should_exit : 1;
128 : : bool m_force_gc : 1;
129 : : bool m_draining_job_queue : 1;
130 : : bool m_should_profile : 1;
131 : : bool m_exec_as_module : 1;
132 : : bool m_unhandled_exception : 1;
133 : : bool m_should_listen_sigusr2 : 1;
134 : :
135 : : void schedule_gc_internal(bool force_gc);
136 : : static gboolean trigger_gc_if_needed(void* data);
137 : : void on_garbage_collection(JSGCStatus, JS::GCReason);
138 : :
139 : : class SavedQueue;
140 : : void start_draining_job_queue(void);
141 : : void stop_draining_job_queue(void);
142 : :
143 : : void warn_about_unhandled_promise_rejections();
144 : :
145 : : GJS_JSAPI_RETURN_CONVENTION bool run_main_loop_hook();
146 : : [[nodiscard]]
147 : : Gjs::GErrorResult<> handle_exit_code(bool no_sync_error_pending,
148 : : const char* source_type,
149 : : const char* identifier,
150 : : uint8_t* exit_code);
151 : : [[nodiscard]] bool auto_profile_enter(void);
152 : : void auto_profile_exit(bool status);
153 : :
154 : : class AutoResetExit {
155 : : GjsContextPrivate* m_self;
156 : :
157 : : public:
158 : 381 : explicit AutoResetExit(GjsContextPrivate* self) { m_self = self; }
159 : 379 : ~AutoResetExit() {
160 : 379 : m_self->m_should_exit = false;
161 : 379 : m_self->m_exit_code = 0;
162 : 379 : }
163 : : };
164 : :
165 : : public:
166 : : /* Retrieving a GjsContextPrivate from JSContext or GjsContext */
167 : 183107 : [[nodiscard]] static GjsContextPrivate* from_cx(JSContext* cx) {
168 : 183107 : return static_cast<GjsContextPrivate*>(JS_GetContextPrivate(cx));
169 : : }
170 : : [[nodiscard]] static GjsContextPrivate* from_object(
171 : : GObject* public_context);
172 : : [[nodiscard]] static GjsContextPrivate* from_object(
173 : : GjsContext* public_context);
174 : : [[nodiscard]] static GjsContextPrivate* from_current_context();
175 : :
176 : : GjsContextPrivate(JSContext* cx, GjsContext* public_context);
177 : : ~GjsContextPrivate(void);
178 : :
179 : : /* Accessors */
180 : : [[nodiscard]] GjsContext* public_context() const {
181 : : return m_public_context;
182 : : }
183 : : [[nodiscard]] bool set_main_loop_hook(JSObject* callable);
184 : 829 : [[nodiscard]] bool has_main_loop_hook() { return !!m_main_loop_hook; }
185 : 44748 : [[nodiscard]] JSContext* context() const { return m_cx; }
186 : 24113 : [[nodiscard]] JSObject* global() const { return m_global.get(); }
187 : 2827 : [[nodiscard]] JSObject* internal_global() const {
188 : 2827 : return m_internal_global.get();
189 : : }
190 : 696 : void main_loop_hold() { m_main_loop.hold(); }
191 : 690 : void main_loop_release() { m_main_loop.release(); }
192 : 2421 : [[nodiscard]] GjsProfiler* profiler() const { return m_profiler; }
193 : 26358 : [[nodiscard]] const GjsAtoms& atoms() const { return *m_atoms; }
194 : 4071 : [[nodiscard]] bool destroying() const { return m_destroying.load(); }
195 : 78 : [[nodiscard]] const char* program_name() const { return m_program_name; }
196 : 257 : void set_program_name(char* value) { m_program_name = value; }
197 : 78 : GJS_USE const char* program_path(void) const { return m_program_path; }
198 : 535 : GJS_USE const char* repl_history_path() const {
199 : 535 : return m_repl_history_path;
200 : : }
201 : 257 : void set_program_path(char* value) { m_program_path = value; }
202 : 257 : void set_search_path(char** value) { m_search_path = value; }
203 : 257 : void set_should_profile(bool value) { m_should_profile = value; }
204 : 257 : void set_execute_as_module(bool value) { m_exec_as_module = value; }
205 : 257 : void set_should_listen_sigusr2(bool value) {
206 : 257 : m_should_listen_sigusr2 = value;
207 : 257 : }
208 : 257 : void set_repl_history_path(char* value) { m_repl_history_path = value; }
209 : : void set_args(std::vector<std::string>&& args);
210 : : GJS_JSAPI_RETURN_CONVENTION JSObject* build_args_array();
211 : 13186 : [[nodiscard]] bool is_owner_thread() const {
212 : 13186 : return m_owner_thread == std::this_thread::get_id();
213 : : }
214 : 41 : [[nodiscard]] JS::WeakCache<FundamentalTable>& fundamental_table() {
215 : 41 : return *m_fundamental_table;
216 : : }
217 : 6738 : [[nodiscard]] JS::WeakCache<GTypeTable>& gtype_table() {
218 : 6738 : return *m_gtype_table;
219 : : }
220 : 2579 : [[nodiscard]] ObjectInitList& object_init_list() {
221 : 2579 : return m_object_init_list;
222 : : }
223 : 87823 : [[nodiscard]] static const GjsAtoms& atoms(JSContext* cx) {
224 : 87823 : return *(from_cx(cx)->m_atoms);
225 : : }
226 : : [[nodiscard]] static JSObject* global(JSContext* cx) {
227 : : return from_cx(cx)->global();
228 : : }
229 : :
230 : : void register_non_module_sourcemap(const char* script,
231 : : const char* filename);
232 : :
233 : : [[nodiscard]]
234 : : Gjs::GErrorResult<> eval(const char* script, size_t script_len,
235 : : const char* filename, int* exit_status_p);
236 : : GJS_JSAPI_RETURN_CONVENTION
237 : : bool eval_with_scope(JS::HandleObject scope_object, const char* script,
238 : : size_t script_len, const char* filename,
239 : : JS::MutableHandleValue retval);
240 : : [[nodiscard]]
241 : : Gjs::GErrorResult<> eval_module(const char* identifier,
242 : : uint8_t* exit_code_p);
243 : : GJS_JSAPI_RETURN_CONVENTION
244 : : bool call_function(JS::HandleObject this_obj, JS::HandleValue func_val,
245 : : const JS::HandleValueArray& args,
246 : : JS::MutableHandleValue rval);
247 : :
248 : 1496 : void schedule_gc(void) { schedule_gc_internal(true); }
249 : : void schedule_gc_if_needed(void);
250 : :
251 : 3 : void report_unhandled_exception() { m_unhandled_exception = true; }
252 : : void exit(uint8_t exit_code);
253 : : [[nodiscard]] bool should_exit(uint8_t* exit_code_p) const;
254 : : [[noreturn]] void exit_immediately(uint8_t exit_code);
255 : :
256 : : // Implementations of JS::JobQueue virtual functions
257 : : GJS_JSAPI_RETURN_CONVENTION
258 : : JSObject* getIncumbentGlobal(JSContext* cx) override;
259 : : GJS_JSAPI_RETURN_CONVENTION
260 : : bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise,
261 : : JS::HandleObject job,
262 : : JS::HandleObject allocation_site,
263 : : JS::HandleObject incumbent_global) override;
264 : : void runJobs(JSContext* cx) override;
265 : 11255 : [[nodiscard]] bool empty() const override { return m_job_queue.empty(); }
266 : 3169 : [[nodiscard]] bool isDrainingStopped() const override {
267 : 3169 : return !m_draining_job_queue;
268 : : }
269 : : js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue(
270 : : JSContext* cx) override;
271 : :
272 : : GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible();
273 : : void register_unhandled_promise_rejection(uint64_t id,
274 : : JS::UniqueChars&& stack);
275 : : void unregister_unhandled_promise_rejection(uint64_t id);
276 : : GJS_JSAPI_RETURN_CONVENTION bool queue_finalization_registry_cleanup(
277 : : JSFunction* cleanup_task);
278 : : GJS_JSAPI_RETURN_CONVENTION bool run_finalization_registry_cleanup();
279 : :
280 : : void register_notifier(DestroyNotify notify_func, void* data);
281 : : void unregister_notifier(DestroyNotify notify_func, void* data);
282 : : void async_closure_enqueue_for_gc(Gjs::Closure*);
283 : :
284 : : [[nodiscard]]
285 : : Gjs::GErrorResult<> register_module(const char* identifier,
286 : : const char* filename);
287 : :
288 : : static void trace(JSTracer* trc, void* data);
289 : :
290 : : void free_profiler(void);
291 : : void dispose(void);
292 : : };
293 : :
294 : : std::string gjs_dumpstack_string();
295 : :
296 : : namespace Gjs {
297 : : class AutoMainRealm : public JSAutoRealm {
298 : : public:
299 : : explicit AutoMainRealm(GjsContextPrivate* gjs);
300 : : explicit AutoMainRealm(JSContext* cx);
301 : : };
302 : :
303 : : class AutoInternalRealm : public JSAutoRealm {
304 : : public:
305 : : explicit AutoInternalRealm(GjsContextPrivate* gjs);
306 : : explicit AutoInternalRealm(JSContext* cx);
307 : : };
308 : : } // namespace Gjs
309 : :
310 : : #endif // GJS_CONTEXT_PRIVATE_H_
|