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 <stdint.h> 11 #include <sys/types.h> // for ssize_t 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 <glib-object.h> 21 #include <glib.h> 22 23 #include <js/GCAPI.h> 24 #include <js/GCHashTable.h> 25 #include <js/GCVector.h> 26 #include <js/HashTable.h> // for DefaultHasher 27 #include <js/Promise.h> 28 #include <js/RootingAPI.h> 29 #include <js/TypeDecls.h> 30 #include <js/UniquePtr.h> 31 #include <js/ValueArray.h> 32 #include <jsapi.h> // for JS_GetContextPrivate 33 #include <jsfriendapi.h> // for ScriptEnvironmentPreparer 34 35 #include "gi/closure.h" 36 #include "gjs/context.h" 37 #include "gjs/jsapi-util.h" 38 #include "gjs/macros.h" 39 #include "gjs/profiler.h" 40 41 namespace js { 42 class SystemAllocPolicy; 43 } 44 class GjsAtoms; 45 class JSTracer; 46 47 using JobQueueStorage = 48 JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>; 49 using ObjectInitList = 50 JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>; 51 using FundamentalTable = 52 JS::GCHashMap<void*, JS::Heap<JSObject*>, js::DefaultHasher<void*>, 53 js::SystemAllocPolicy>; 54 using GTypeTable = 55 JS::GCHashMap<GType, JS::Heap<JSObject*>, js::DefaultHasher<GType>, 56 js::SystemAllocPolicy>; 57 58 class GjsContextPrivate : public JS::JobQueue { 59 public: 60 using DestroyNotify = void (*)(JSContext*, void* data); 61 62 private: 63 GjsContext* m_public_context; 64 JSContext* m_cx; 65 JS::Heap<JSObject*> m_global; 66 JS::Heap<JSObject*> m_internal_global; 67 std::thread::id m_owner_thread; 68 69 char* m_program_name; 70 char* m_program_path; 71 72 char** m_search_path; 73 74 unsigned m_auto_gc_id; 75 76 GjsAtoms* m_atoms; 77 78 std::vector<std::string> m_args; 79 80 JobQueueStorage m_job_queue; 81 unsigned m_idle_drain_handler; 82 83 std::vector<std::pair<DestroyNotify, void*>> m_destroy_notifications; 84 std::vector<Gjs::Closure::Ptr> m_async_closures; 85 std::unordered_map<uint64_t, GjsAutoChar> m_unhandled_rejection_stacks; 86 87 GjsProfiler* m_profiler; 88 89 /* Environment preparer needed for debugger, taken from SpiderMonkey's 90 * JS shell */ 91 struct EnvironmentPreparer final : protected js::ScriptEnvironmentPreparer { 92 JSContext* m_cx; 93 EnvironmentPreparerfinal94 explicit EnvironmentPreparer(JSContext* cx) : m_cx(cx) { 95 js::SetScriptEnvironmentPreparer(m_cx, this); 96 } 97 98 void invoke(JS::HandleObject scope, Closure& closure) override; 99 }; 100 EnvironmentPreparer m_environment_preparer; 101 102 // Weak pointer mapping from fundamental native pointer to JSObject 103 JS::WeakCache<FundamentalTable>* m_fundamental_table; 104 JS::WeakCache<GTypeTable>* m_gtype_table; 105 106 // List that holds JSObject GObject wrappers for JS-created classes, from 107 // the time of their creation until their GObject instance init function is 108 // called 109 ObjectInitList m_object_init_list; 110 111 uint8_t m_exit_code; 112 113 /* flags */ 114 std::atomic_bool m_destroying = ATOMIC_VAR_INIT(false); 115 bool m_in_gc_sweep : 1; 116 bool m_should_exit : 1; 117 bool m_force_gc : 1; 118 bool m_draining_job_queue : 1; 119 bool m_should_profile : 1; 120 bool m_exec_as_module : 1; 121 bool m_should_listen_sigusr2 : 1; 122 123 // If profiling is enabled, we record the durations and reason for GC mark 124 // and sweep 125 int64_t m_gc_begin_time; 126 int64_t m_sweep_begin_time; 127 int64_t m_group_sweep_begin_time; 128 const char* m_gc_reason; // statically allocated 129 130 void schedule_gc_internal(bool force_gc); 131 static gboolean trigger_gc_if_needed(void* data); 132 void on_garbage_collection(JSGCStatus, JS::GCReason); 133 134 class SavedQueue; 135 void start_draining_job_queue(void); 136 void stop_draining_job_queue(void); 137 static gboolean drain_job_queue_idle_handler(void* data); 138 139 uint8_t handle_exit_code(const char* type, const char* identifier, 140 GError** error); 141 [[nodiscard]] bool auto_profile_enter(void); 142 void auto_profile_exit(bool status); 143 144 class AutoResetExit { 145 GjsContextPrivate* m_self; 146 147 public: AutoResetExit(GjsContextPrivate * self)148 explicit AutoResetExit(GjsContextPrivate* self) { m_self = self; } ~AutoResetExit()149 ~AutoResetExit() { 150 m_self->m_should_exit = false; 151 m_self->m_exit_code = 0; 152 } 153 }; 154 155 public: 156 /* Retrieving a GjsContextPrivate from JSContext or GjsContext */ from_cx(JSContext * cx)157 [[nodiscard]] static GjsContextPrivate* from_cx(JSContext* cx) { 158 return static_cast<GjsContextPrivate*>(JS_GetContextPrivate(cx)); 159 } 160 [[nodiscard]] static GjsContextPrivate* from_object( 161 GObject* public_context); 162 [[nodiscard]] static GjsContextPrivate* from_object( 163 GjsContext* public_context); 164 [[nodiscard]] static GjsContextPrivate* from_current_context(); 165 166 GjsContextPrivate(JSContext* cx, GjsContext* public_context); 167 ~GjsContextPrivate(void); 168 169 /* Accessors */ public_context()170 [[nodiscard]] GjsContext* public_context() const { 171 return m_public_context; 172 } context()173 [[nodiscard]] JSContext* context() const { return m_cx; } global()174 [[nodiscard]] JSObject* global() const { return m_global.get(); } internal_global()175 [[nodiscard]] JSObject* internal_global() const { 176 return m_internal_global.get(); 177 } profiler()178 [[nodiscard]] GjsProfiler* profiler() const { return m_profiler; } atoms()179 [[nodiscard]] const GjsAtoms& atoms() const { return *m_atoms; } destroying()180 [[nodiscard]] bool destroying() const { return m_destroying.load(); } sweeping()181 [[nodiscard]] bool sweeping() const { return m_in_gc_sweep; } program_name()182 [[nodiscard]] const char* program_name() const { return m_program_name; } set_program_name(char * value)183 void set_program_name(char* value) { m_program_name = value; } program_path(void)184 GJS_USE const char* program_path(void) const { return m_program_path; } set_program_path(char * value)185 void set_program_path(char* value) { m_program_path = value; } set_search_path(char ** value)186 void set_search_path(char** value) { m_search_path = value; } set_should_profile(bool value)187 void set_should_profile(bool value) { m_should_profile = value; } set_execute_as_module(bool value)188 void set_execute_as_module(bool value) { m_exec_as_module = value; } set_should_listen_sigusr2(bool value)189 void set_should_listen_sigusr2(bool value) { 190 m_should_listen_sigusr2 = value; 191 } 192 void set_args(std::vector<std::string>&& args); 193 GJS_JSAPI_RETURN_CONVENTION JSObject* build_args_array(); is_owner_thread()194 [[nodiscard]] bool is_owner_thread() const { 195 return m_owner_thread == std::this_thread::get_id(); 196 } fundamental_table()197 [[nodiscard]] JS::WeakCache<FundamentalTable>& fundamental_table() { 198 return *m_fundamental_table; 199 } gtype_table()200 [[nodiscard]] JS::WeakCache<GTypeTable>& gtype_table() { 201 return *m_gtype_table; 202 } object_init_list()203 [[nodiscard]] ObjectInitList& object_init_list() { 204 return m_object_init_list; 205 } atoms(JSContext * cx)206 [[nodiscard]] static const GjsAtoms& atoms(JSContext* cx) { 207 return *(from_cx(cx)->m_atoms); 208 } 209 210 [[nodiscard]] bool eval(const char* script, ssize_t script_len, 211 const char* filename, int* exit_status_p, 212 GError** error); 213 GJS_JSAPI_RETURN_CONVENTION 214 bool eval_with_scope(JS::HandleObject scope_object, const char* script, 215 ssize_t script_len, const char* filename, 216 JS::MutableHandleValue retval); 217 [[nodiscard]] bool eval_module(const char* identifier, uint8_t* exit_code_p, 218 GError** error); 219 GJS_JSAPI_RETURN_CONVENTION 220 bool call_function(JS::HandleObject this_obj, JS::HandleValue func_val, 221 const JS::HandleValueArray& args, 222 JS::MutableHandleValue rval); 223 schedule_gc(void)224 void schedule_gc(void) { schedule_gc_internal(true); } 225 void schedule_gc_if_needed(void); 226 227 void exit(uint8_t exit_code); 228 [[nodiscard]] bool should_exit(uint8_t* exit_code_p) const; 229 230 // Implementations of JS::JobQueue virtual functions 231 GJS_JSAPI_RETURN_CONVENTION 232 JSObject* getIncumbentGlobal(JSContext* cx) override; 233 GJS_JSAPI_RETURN_CONVENTION 234 bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, 235 JS::HandleObject job, 236 JS::HandleObject allocation_site, 237 JS::HandleObject incumbent_global) override; 238 void runJobs(JSContext* cx) override; empty()239 [[nodiscard]] bool empty() const override { return m_job_queue.empty(); } 240 js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue( 241 JSContext* cx) override; 242 243 GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible(void); 244 void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack); 245 void unregister_unhandled_promise_rejection(uint64_t id); 246 void warn_about_unhandled_promise_rejections(); 247 248 void register_notifier(DestroyNotify notify_func, void* data); 249 void unregister_notifier(DestroyNotify notify_func, void* data); 250 void async_closure_enqueue_for_gc(Gjs::Closure*); 251 252 [[nodiscard]] bool register_module(const char* identifier, 253 const char* filename, GError** error); 254 255 void set_gc_status(JSGCStatus status, JS::GCReason reason); 256 void set_finalize_status(JSFinalizeStatus status); 257 258 static void trace(JSTracer* trc, void* data); 259 260 void free_profiler(void); 261 void dispose(void); 262 }; 263 #endif // GJS_CONTEXT_PRIVATE_H_ 264