Repository navigation
Expand file tree
/
Copy pathCaches.h
More file actions
323 lines (282 loc) · 13.4 KB
/
Copy pathCaches.h
File metadata and controls
323 lines (282 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#ifndef Caches_h
#define Caches_h
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "Common.h"
#include "ConcurrentMap.h"
#include "Metadata.h"
#include "robin_hood.h"
namespace tns {
struct StructInfo;
class StructWrapper;
enum class StructStorage;
struct ObjectWeakCallbackState;
class PromiseRejectionTracker;
class IsolateTracked;
// Declaring both halves transparent lets robin_hood probe a map keyed by
// std::string with a string_view, so callers holding a const char* from the
// Obj-C runtime do not have to allocate one just to look up.
struct TransparentStringHash {
using is_transparent = void;
size_t operator()(std::string_view key) const {
return robin_hood::hash_bytes(key.data(), key.size());
}
};
struct TransparentStringEqual {
using is_transparent = void;
bool operator()(std::string_view lhs, std::string_view rhs) const {
return lhs == rhs;
}
};
class Caches {
public:
class WorkerState {
public:
WorkerState(v8::Isolate* isolate,
std::shared_ptr<v8::Persistent<v8::Value>> poWorker,
void* userData)
: isolate_(isolate), poWorker_(poWorker), userData_(userData) {}
v8::Isolate* GetIsolate() { return this->isolate_; }
std::shared_ptr<v8::Persistent<v8::Value>> GetWorker() {
return this->poWorker_;
}
void* UserData() { return this->userData_; }
private:
v8::Isolate* isolate_;
std::shared_ptr<v8::Persistent<v8::Value>> poWorker_;
void* userData_;
};
Caches(v8::Isolate* isolate, const int& isolateId_ = -1);
~Caches();
bool isWorker = false;
static std::shared_ptr<ConcurrentMap<std::string, const Meta*>> Metadata;
static std::shared_ptr<
ConcurrentMap<int, std::shared_ptr<Caches::WorkerState>>>
Workers;
inline static std::shared_ptr<Caches> Init(v8::Isolate* isolate,
const int& isolateId) {
auto cache = std::make_shared<Caches>(isolate, isolateId);
// create a new shared_ptr that will live until Remove is called
isolate->SetData(0, static_cast<void*>(new std::shared_ptr<Caches>(cache)));
return cache;
}
inline static std::shared_ptr<Caches> Get(v8::Isolate* isolate) {
auto cache = isolate->GetData(0);
if (cache != nullptr) {
return *reinterpret_cast<std::shared_ptr<Caches>*>(cache);
}
// this should only happen when an isolate is accessed after disposal
// so we return a dummy cache
return std::make_shared<Caches>(isolate);
}
static void Remove(v8::Isolate* isolate);
// Per-isolate storage for subsystem state, so a subsystem can declare its
// state struct in its own .cpp instead of adding members here (and instead
// of a process-wide map keyed by v8::Isolate*, whose container is shared
// across runtimes tearing down on their own threads). The first call
// default-constructs T; it is destroyed with this Caches, under the teardown
// Locker while the isolate is still alive, so T may hold v8::Persistent
// members. Returns null once teardown has begun (InvalidateIsolate) or the
// isolate's Caches is gone — callers must bail, not recreate state. Access
// from the isolate's thread only, like every other member.
template <typename T>
static T* StateFor(v8::Isolate* isolate) {
auto raw = isolate->GetData(0);
if (raw == nullptr) {
return nullptr;
}
Caches* cache = reinterpret_cast<std::shared_ptr<Caches>*>(raw)->get();
if (!cache->IsValid()) {
return nullptr;
}
return cache->GetOrCreateState<T>();
}
inline int getIsolateId() { return isolateId_; }
// The id assigned at Init; unlike getIsolateId() it survives
// InvalidateIsolate, so it still names this isolate's gate during teardown.
// -1 for the stand-in Get() returns once the isolate's Caches is gone.
inline int getGateId() const { return gateId_; }
inline void InvalidateIsolate() { isolateId_ = -1; }
inline bool IsValid() { return isolateId_ != -1; }
void SetContext(v8::Local<v8::Context> context);
v8::Local<v8::Context> GetContext();
// GetContext crashes before SetContext; work can run in that window (v8
// posts foreground tasks during Isolate::New)
inline bool HasContext() { return context_ != nullptr; }
// Per-isolate unhandled promise rejection tracking. Fed by
// NativeScriptException::OnPromiseRejected and drained once per runloop turn.
std::unique_ptr<PromiseRejectionTracker> PromiseRejections;
// Head of the intrusive list of wrappers registered by
// ObjectManager::Register(). V8 no longer offers a way to enumerate
// persistent handles, so teardown disposal walks this instead. Entries unlink
// themselves when the wrapper is disposed, so the list only ever holds live
// wrappers.
ObjectWeakCallbackState* ObjectManagedValues = nullptr;
robin_hood::unordered_map<const Meta*,
std::unique_ptr<v8::Persistent<v8::Value>>>
Prototypes;
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Object>>,
TransparentStringHash, TransparentStringEqual>
ClassPrototypes;
robin_hood::unordered_map<const Meta*,
std::unique_ptr<v8::Persistent<v8::Object>>>
MetaInstancePrototypes;
robin_hood::unordered_map<
const BaseClassMeta*,
std::unique_ptr<v8::Persistent<v8::FunctionTemplate>>>
CtorFuncTemplates;
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Function>>>
CtorFuncs;
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Function>>>
ProtocolCtorFuncs;
robin_hood::unordered_map<BinaryTypeEncodingType,
std::unique_ptr<v8::Persistent<v8::Object>>>
PrimitiveInteropTypes;
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Function>>>
CFunctions;
robin_hood::unordered_map<id, std::shared_ptr<v8::Persistent<v8::Value>>>
Instances;
robin_hood::unordered_map<const void*,
std::shared_ptr<v8::Persistent<v8::Object>>>
PointerInstances;
// ExternalMemory's size estimator per class, resolved on first sight.
robin_hood::unordered_map<Class, uint8_t> ExternalSizeEstimators;
// Live IsolateTracked instances (URL, URLSearchParams, URLPattern). Their
// weak-callback finalizers never fire at isolate disposal, so teardown
// sweeps this set to delete whatever GC didn't get to.
robin_hood::unordered_set<IsolateTracked*> TrackedInstances;
std::function<v8::Local<v8::FunctionTemplate>(
v8::Local<v8::Context>, const BaseClassMeta*, KnownUnknownClassPair,
const std::vector<std::string>&)>
ObjectCtorInitializer;
// Builds a struct object over `data`: a copy of it (kCopy; zero-filled when
// `data` is null) or an alias kept alive by `parent` (kAlias). Indirected
// through std::function so this header stays free of MetadataBuilder.
std::function<v8::Local<v8::Object>(v8::Local<v8::Context>, const StructInfo&,
void* data, StructStorage storage,
v8::Local<v8::Object> parent)>
StructInstanceFactory;
robin_hood::unordered_map<const InterfaceMeta*,
std::vector<const MethodMeta*>>
Initializers;
std::unique_ptr<v8::Persistent<v8::Function>> EmptyObjCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> SliceFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> OriginalExtendsFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> WeakRefGetterFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> WeakRefClearFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
// console formatter (internal/inspect.js), initialized by Console::Init.
std::unique_ptr<v8::Persistent<v8::Function>> InspectFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
// ns:util's format, used by console.* for %-substitution.
std::unique_ptr<v8::Persistent<v8::Function>> FormatFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
bool FormatFuncUnavailable = false;
std::unique_ptr<v8::Persistent<v8::Function>> InteropReferenceCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> PointerCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> FunctionReferenceCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> UnmanagedTypeCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
// Synthetic ES modules wrapping the `ns:`/`node:` builtin modules
// (NsBuiltinModules), keyed by specifier. Per isolate: a builtin module is a
// singleton per realm, so workers get their own. The exports they wrap live
// in the per-isolate builtin exports cache (BuiltinLoader::GetExports).
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Module>>>
BuiltinModules;
// Specifiers currently being built, so a shim requiring back into the module
// that is loading it fails instead of recursing.
robin_hood::unordered_set<std::string> BuiltinModulesInProgress;
// The `require` handed to every builtin, resolving builtin specifiers only.
std::unique_ptr<v8::Persistent<v8::Function>> BuiltinRequire =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
// Frozen intrinsics snapshot returned by internal/primordials.js, passed to
// every builtin as its second fixed parameter (BuiltinLoader::RunBuiltin).
// Per isolate, so workers snapshot their own realm's intrinsics.
std::unique_ptr<v8::Persistent<v8::Object>> Primordials =
std::unique_ptr<v8::Persistent<v8::Object>>(nullptr);
// Internal EventTarget instance backing the global, returned by the generic
// event-primitives bootstrap IIFE (Events::Init). Holds the real listener
// store, so native layers dispatch through it without going through
// overwritable globals. Cleaned up with the other Persistent members when
// Caches is destroyed, before isolate disposal.
std::unique_ptr<v8::Persistent<v8::Object>> GlobalEventTarget =
std::unique_ptr<v8::Persistent<v8::Object>>(nullptr);
// Phase 2 WHATWG error-events dispatch closures returned by the bootstrap
// IIFE (ErrorEvents::Init). They close over the internal listener store, so
// native dispatch keeps working even if app code overwrites
// globalThis.dispatchEvent. Cleaned up with the other Persistent members when
// Caches is destroyed, before isolate disposal.
std::unique_ptr<v8::Persistent<v8::Function>> DispatchErrorEventFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> DispatchUnhandledRejectionFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> DispatchRejectionHandledFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>>
DispatchReleasedNativeAccessFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
// Phase 3 per-isolate brand for interop.escapeException. An isolate-private
// symbol (v8::Private, not a plain Symbol) so user code cannot discover or
// forge it. Created lazily via ArgConverter::GetEscapeExceptionBrand and used
// to mark/extract escaped native exceptions across JS<->native boundaries.
std::unique_ptr<v8::Persistent<v8::Private>> EscapeExceptionBrand =
std::unique_ptr<v8::Persistent<v8::Private>>(nullptr);
using unique_void_ptr = std::unique_ptr<void, void (*)(void const*)>;
template <typename T>
auto unique_void(T* ptr) -> unique_void_ptr {
return unique_void_ptr(ptr, [](void const* data) {
T const* p = static_cast<T const*>(data);
delete p;
});
}
std::vector<unique_void_ptr> cacheBoundObjects_;
template <typename T>
void registerCacheBoundObject(T* ptr) {
this->cacheBoundObjects_.push_back(unique_void(ptr));
}
private:
// One slot index per state type, handed out process-wide on first use.
static size_t NextStateSlotIndex();
template <typename T>
static size_t StateSlotIndexFor() {
static const size_t index = NextStateSlotIndex();
return index;
}
// Type-erased so Caches need not know any subsystem's state type; the
// deleter restores the type at destruction.
using StateSlot = std::unique_ptr<void, void (*)(void*)>;
template <typename T>
T* GetOrCreateState() {
size_t index = StateSlotIndexFor<T>();
while (this->stateSlots_.size() <= index) {
this->stateSlots_.emplace_back(nullptr, [](void*) {});
}
StateSlot& slot = this->stateSlots_[index];
if (slot == nullptr) {
slot = StateSlot(new T(),
[](void* value) { delete static_cast<T*>(value); });
}
return static_cast<T*>(slot.get());
}
std::vector<StateSlot> stateSlots_;
v8::Isolate* isolate_;
std::shared_ptr<v8::Persistent<v8::Context>> context_;
int isolateId_;
const int gateId_;
};
} // namespace tns
#endif /* Caches_h */