1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ 2 /* 3 * Copyright (c) 2014 Colin Walters <walters@verbum.org> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to 7 * deal in the Software without restriction, including without limitation the 8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 * sell copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24 #ifndef GJS_CONTEXT_PRIVATE_H_ 25 #define GJS_CONTEXT_PRIVATE_H_ 26 27 #include <config.h> 28 29 #include <stdint.h> 30 #include <sys/types.h> // for ssize_t 31 32 #include <type_traits> // for is_same 33 #include <unordered_map> 34 35 #include <glib-object.h> 36 #include <glib.h> 37 38 #include <js/GCHashTable.h> 39 #include <js/GCVector.h> 40 #include <js/Promise.h> 41 #include <js/RootingAPI.h> 42 #include <js/TypeDecls.h> 43 #include <js/ValueArray.h> 44 #include <jsapi.h> // for JS_GetContextPrivate 45 #include <jsfriendapi.h> // for ScriptEnvironmentPreparer 46 #include <mozilla/HashTable.h> // for DefaultHasher 47 #include <mozilla/UniquePtr.h> 48 49 #include "cjs/context.h" 50 #include "cjs/jsapi-util.h" 51 #include "cjs/macros.h" 52 #include "cjs/profiler.h" 53 54 namespace js { 55 class SystemAllocPolicy; 56 } 57 class GjsAtoms; 58 class JSTracer; 59 60 using JobQueueStorage = 61 JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>; 62 using ObjectInitList = 63 JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>; 64 using FundamentalTable = 65 JS::GCHashMap<void*, JS::Heap<JSObject*>, js::DefaultHasher<void*>, 66 js::SystemAllocPolicy>; 67 using GTypeTable = 68 JS::GCHashMap<GType, JS::Heap<JSObject*>, js::DefaultHasher<GType>, 69 js::SystemAllocPolicy>; 70 71 struct Dummy {}; 72 using GTypeNotUint64 = 73 std::conditional_t<!std::is_same_v<GType, uint64_t>, GType, Dummy>; 74 75 // The GC sweep method should ignore FundamentalTable and GTypeTable's key types 76 namespace JS { 77 // Forward declarations 78 template <typename T> 79 class WeakCache; 80 template <typename T> 81 struct GCPolicy; 82 83 template <> 84 struct GCPolicy<void*> : public IgnoreGCPolicy<void*> {}; 85 // We need GCPolicy<GType> for GTypeTable. SpiderMonkey already defines 86 // GCPolicy<uint64_t> which is equal to GType on some systems; for others we 87 // need to define it. (macOS's uint64_t is unsigned long long, which is a 88 // different type from unsigned long, even if they are the same width) 89 template <> 90 struct GCPolicy<GTypeNotUint64> : public IgnoreGCPolicy<GTypeNotUint64> {}; 91 } // namespace JS 92 93 class GjsContextPrivate : public JS::JobQueue { 94 GjsContext* m_public_context; 95 JSContext* m_cx; 96 JS::Heap<JSObject*> m_global; 97 GThread* m_owner_thread; 98 99 char* m_program_name; 100 101 char** m_search_path; 102 103 unsigned m_auto_gc_id; 104 105 GjsAtoms* m_atoms; 106 107 JobQueueStorage m_job_queue; 108 unsigned m_idle_drain_handler; 109 110 std::unordered_map<uint64_t, GjsAutoChar> m_unhandled_rejection_stacks; 111 112 GjsProfiler* m_profiler; 113 114 /* Environment preparer needed for debugger, taken from SpiderMonkey's 115 * JS shell */ 116 struct EnvironmentPreparer final : protected js::ScriptEnvironmentPreparer { 117 JSContext* m_cx; 118 119 explicit EnvironmentPreparer(JSContext* cx) : m_cx(cx) { 120 js::SetScriptEnvironmentPreparer(m_cx, this); 121 } 122 123 void invoke(JS::HandleObject scope, Closure& closure) override; 124 }; 125 EnvironmentPreparer m_environment_preparer; 126 127 // Weak pointer mapping from fundamental native pointer to JSObject 128 JS::WeakCache<FundamentalTable>* m_fundamental_table; 129 JS::WeakCache<GTypeTable>* m_gtype_table; 130 131 // List that holds JSObject GObject wrappers for JS-created classes, from 132 // the time of their creation until their GObject instance init function is 133 // called 134 ObjectInitList m_object_init_list; 135 136 uint8_t m_exit_code; 137 138 /* flags */ 139 bool m_destroying : 1; 140 bool m_in_gc_sweep : 1; 141 bool m_should_exit : 1; 142 bool m_force_gc : 1; 143 bool m_draining_job_queue : 1; 144 bool m_should_profile : 1; 145 bool m_should_listen_sigusr2 : 1; 146 147 int64_t m_sweep_begin_time; 148 149 void schedule_gc_internal(bool force_gc); 150 static gboolean trigger_gc_if_needed(void* data); 151 152 class SavedQueue; 153 void start_draining_job_queue(void); 154 void stop_draining_job_queue(void); 155 static gboolean drain_job_queue_idle_handler(void* data); 156 157 void warn_about_unhandled_promise_rejections(void); 158 159 class AutoResetExit { 160 GjsContextPrivate* m_self; 161 162 public: 163 explicit AutoResetExit(GjsContextPrivate* self) { m_self = self; } 164 ~AutoResetExit() { 165 m_self->m_should_exit = false; 166 m_self->m_exit_code = 0; 167 } 168 }; 169 170 public: 171 /* Retrieving a GjsContextPrivate from JSContext or GjsContext */ 172 [[nodiscard]] static GjsContextPrivate* from_cx(JSContext* cx) { 173 return static_cast<GjsContextPrivate*>(JS_GetContextPrivate(cx)); 174 } 175 [[nodiscard]] static GjsContextPrivate* from_object( 176 GObject* public_context); 177 [[nodiscard]] static GjsContextPrivate* from_object( 178 GjsContext* public_context); 179 [[nodiscard]] static GjsContextPrivate* from_current_context(); 180 181 GjsContextPrivate(JSContext* cx, GjsContext* public_context); 182 ~GjsContextPrivate(void); 183 184 /* Accessors */ 185 [[nodiscard]] GjsContext* public_context() const { 186 return m_public_context; 187 } 188 [[nodiscard]] JSContext* context() const { return m_cx; } 189 [[nodiscard]] JSObject* global() const { return m_global.get(); } 190 [[nodiscard]] GjsProfiler* profiler() const { return m_profiler; } 191 [[nodiscard]] const GjsAtoms& atoms() const { return *m_atoms; } 192 [[nodiscard]] bool destroying() const { return m_destroying; } 193 [[nodiscard]] bool sweeping() const { return m_in_gc_sweep; } 194 [[nodiscard]] const char* program_name() const { return m_program_name; } 195 void set_program_name(char* value) { m_program_name = value; } 196 void set_search_path(char** value) { m_search_path = value; } 197 void set_should_profile(bool value) { m_should_profile = value; } 198 void set_should_listen_sigusr2(bool value) { 199 m_should_listen_sigusr2 = value; 200 } 201 [[nodiscard]] bool is_owner_thread() const { 202 return m_owner_thread == g_thread_self(); 203 } 204 [[nodiscard]] JS::WeakCache<FundamentalTable>& fundamental_table() { 205 return *m_fundamental_table; 206 } 207 [[nodiscard]] JS::WeakCache<GTypeTable>& gtype_table() { 208 return *m_gtype_table; 209 } 210 [[nodiscard]] ObjectInitList& object_init_list() { 211 return m_object_init_list; 212 } 213 [[nodiscard]] static const GjsAtoms& atoms(JSContext* cx) { 214 return *(from_cx(cx)->m_atoms); 215 } 216 217 GJS_JSAPI_RETURN_CONVENTION 218 bool eval(const char* script, ssize_t script_len, const char* filename, 219 int* exit_status_p, GError** error); 220 GJS_JSAPI_RETURN_CONVENTION 221 bool eval_with_scope(JS::HandleObject scope_object, const char* script, 222 ssize_t script_len, const char* filename, 223 JS::MutableHandleValue retval); 224 GJS_JSAPI_RETURN_CONVENTION 225 bool call_function(JS::HandleObject this_obj, JS::HandleValue func_val, 226 const JS::HandleValueArray& args, 227 JS::MutableHandleValue rval); 228 229 void schedule_gc(void) { schedule_gc_internal(true); } 230 void schedule_gc_if_needed(void); 231 232 void exit(uint8_t exit_code); 233 [[nodiscard]] bool should_exit(uint8_t* exit_code_p) const; 234 235 // Implementations of JS::JobQueue virtual functions 236 GJS_JSAPI_RETURN_CONVENTION 237 JSObject* getIncumbentGlobal(JSContext* cx) override; 238 GJS_JSAPI_RETURN_CONVENTION 239 bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, 240 JS::HandleObject job, 241 JS::HandleObject allocation_site, 242 JS::HandleObject incumbent_global) override; 243 void runJobs(JSContext* cx) override; 244 [[nodiscard]] bool empty() const override { return m_job_queue.empty(); } 245 js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue( 246 JSContext* cx) override; 247 248 GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible(void); 249 void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack); 250 void unregister_unhandled_promise_rejection(uint64_t id); 251 252 void set_sweeping(bool value); 253 254 static void trace(JSTracer* trc, void* data); 255 256 void free_profiler(void); 257 void dispose(void); 258 }; 259 #endif // GJS_CONTEXT_PRIVATE_H_ 260