/src/hermes/lib/VM/RuntimeModule.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "hermes/VM/RuntimeModule.h" |
9 | | |
10 | | #include "hermes/BCGen/HBC/BytecodeProviderFromSrc.h" |
11 | | #include "hermes/Support/PerfSection.h" |
12 | | #include "hermes/VM/CodeBlock.h" |
13 | | #include "hermes/VM/Domain.h" |
14 | | #include "hermes/VM/HiddenClass.h" |
15 | | #include "hermes/VM/Predefined.h" |
16 | | #include "hermes/VM/Runtime.h" |
17 | | #include "hermes/VM/RuntimeModule-inline.h" |
18 | | #include "hermes/VM/StringPrimitive.h" |
19 | | #include "hermes/VM/WeakRoot-inline.h" |
20 | | |
21 | | namespace hermes { |
22 | | namespace vm { |
23 | | |
24 | | RuntimeModule::RuntimeModule( |
25 | | Runtime &runtime, |
26 | | Handle<Domain> domain, |
27 | | RuntimeModuleFlags flags, |
28 | | llvh::StringRef sourceURL, |
29 | | facebook::hermes::debugger::ScriptID scriptID) |
30 | 262 | : runtime_(runtime), |
31 | 262 | domain_(*domain, runtime), |
32 | 262 | flags_(flags), |
33 | 262 | sourceURL_(sourceURL), |
34 | 262 | scriptID_(scriptID) { |
35 | 262 | runtime_.addRuntimeModule(this); |
36 | 262 | Domain::addRuntimeModule(domain, runtime, this); |
37 | 262 | #ifndef HERMESVM_LEAN |
38 | 262 | lazyRoot_ = this; |
39 | 262 | #endif |
40 | 262 | } |
41 | | |
42 | | SymbolID RuntimeModule::createSymbolFromStringIDMayAllocate( |
43 | | StringID stringID, |
44 | | const StringTableEntry &entry, |
45 | 13.6k | OptValue<uint32_t> mhash) { |
46 | | // Use manual pointer arithmetic to avoid out of bounds errors on empty |
47 | | // string accesses. |
48 | 13.6k | auto strStorage = bcProvider_->getStringStorage(); |
49 | 13.6k | if (entry.isUTF16()) { |
50 | 236 | const char16_t *s = |
51 | 236 | (const char16_t *)(strStorage.begin() + entry.getOffset()); |
52 | 236 | UTF16Ref str{s, entry.getLength()}; |
53 | 236 | uint32_t hash = mhash ? *mhash : hashString(str); |
54 | 236 | return mapStringMayAllocate(str, stringID, hash); |
55 | 13.4k | } else { |
56 | | // ASCII. |
57 | 13.4k | const char *s = (const char *)strStorage.begin() + entry.getOffset(); |
58 | 13.4k | ASCIIRef str{s, entry.getLength()}; |
59 | 13.4k | uint32_t hash = mhash ? *mhash : hashString(str); |
60 | 13.4k | return mapStringMayAllocate(str, stringID, hash); |
61 | 13.4k | } |
62 | 13.6k | } |
63 | | |
64 | 262 | RuntimeModule::~RuntimeModule() { |
65 | 262 | if (bcProvider_ && !bcProvider_->getRawBuffer().empty()) |
66 | 186 | runtime_.getCrashManager().unregisterMemory(bcProvider_.get()); |
67 | 262 | runtime_.getCrashManager().unregisterMemory(this); |
68 | 262 | runtime_.removeRuntimeModule(this); |
69 | | |
70 | | // We may reference other CodeBlocks through lazy compilation, but we only |
71 | | // own the ones that reference us. |
72 | 9.67k | for (auto *block : functionMap_) { |
73 | 9.67k | if (block != nullptr && block->getRuntimeModule() == this) { |
74 | 3.71k | runtime_.getHeap().getIDTracker().untrackNative(block); |
75 | 3.71k | delete block; |
76 | 3.71k | } |
77 | 9.67k | } |
78 | 262 | runtime_.getHeap().getIDTracker().untrackNative(&functionMap_); |
79 | 262 | } |
80 | | |
81 | 262 | void RuntimeModule::prepareForDestruction() { |
82 | 9.94k | for (int i = 0, e = functionMap_.size(); i < e; i++) { |
83 | 9.67k | if (functionMap_[i] != nullptr && |
84 | 9.67k | functionMap_[i]->getRuntimeModule() != this) { |
85 | 6 | functionMap_[i] = nullptr; |
86 | 6 | } |
87 | 9.67k | } |
88 | 262 | } |
89 | | |
90 | | CallResult<RuntimeModule *> RuntimeModule::create( |
91 | | Runtime &runtime, |
92 | | Handle<Domain> domain, |
93 | | facebook::hermes::debugger::ScriptID scriptID, |
94 | | std::shared_ptr<hbc::BCProvider> &&bytecode, |
95 | | RuntimeModuleFlags flags, |
96 | 163 | llvh::StringRef sourceURL) { |
97 | 163 | RuntimeModule *result; |
98 | 163 | result = new RuntimeModule(runtime, domain, flags, sourceURL, scriptID); |
99 | 163 | runtime.getCrashManager().registerMemory(result, sizeof(*result)); |
100 | 163 | if (bytecode) { |
101 | 163 | if (result->initializeMayAllocate(std::move(bytecode)) == |
102 | 163 | ExecutionStatus::EXCEPTION) { |
103 | 0 | return ExecutionStatus::EXCEPTION; |
104 | 0 | } |
105 | | // If the BC provider is backed by a buffer, register the BC provider struct |
106 | | // (but not the buffer contents, since that might be too large). |
107 | 163 | if (result->bcProvider_ && !result->bcProvider_->getRawBuffer().empty()) |
108 | 93 | runtime.getCrashManager().registerMemory( |
109 | 93 | result->bcProvider_.get(), sizeof(hbc::BCProviderFromBuffer)); |
110 | 163 | } |
111 | 163 | return result; |
112 | 163 | } |
113 | | |
114 | | RuntimeModule *RuntimeModule::createUninitialized( |
115 | | Runtime &runtime, |
116 | | Handle<Domain> domain, |
117 | | RuntimeModuleFlags flags, |
118 | 99 | facebook::hermes::debugger::ScriptID scriptID) { |
119 | 99 | return new RuntimeModule(runtime, domain, flags, "", scriptID); |
120 | 99 | } |
121 | | |
122 | | void RuntimeModule::initializeWithoutCJSModulesMayAllocate( |
123 | 256 | std::shared_ptr<hbc::BCProvider> &&bytecode) { |
124 | 256 | assert(!bcProvider_ && "RuntimeModule already initialized"); |
125 | 256 | bcProvider_ = std::move(bytecode); |
126 | 256 | importStringIDMapMayAllocate(); |
127 | 256 | initializeFunctionMap(); |
128 | 256 | } |
129 | | |
130 | | ExecutionStatus RuntimeModule::initializeMayAllocate( |
131 | 163 | std::shared_ptr<hbc::BCProvider> &&bytecode) { |
132 | 163 | initializeWithoutCJSModulesMayAllocate(std::move(bytecode)); |
133 | 163 | if (LLVM_UNLIKELY(importCJSModuleTable() == ExecutionStatus::EXCEPTION)) { |
134 | 0 | return ExecutionStatus::EXCEPTION; |
135 | 0 | } |
136 | 163 | return ExecutionStatus::RETURNED; |
137 | 163 | } |
138 | | |
139 | 3.71k | CodeBlock *RuntimeModule::getCodeBlockSlowPath(unsigned index) { |
140 | 3.71k | #ifndef HERMESVM_LEAN |
141 | 3.71k | if (bcProvider_->isFunctionLazy(index)) { |
142 | 6 | auto *lazyModule = RuntimeModule::createLazyModule( |
143 | 6 | runtime_, getDomain(runtime_), this, index); |
144 | 6 | functionMap_[index] = lazyModule->getOnlyLazyCodeBlock(); |
145 | 6 | return functionMap_[index]; |
146 | 6 | } |
147 | 3.71k | #endif |
148 | 3.71k | functionMap_[index] = CodeBlock::createCodeBlock( |
149 | 3.71k | this, |
150 | 3.71k | bcProvider_->getFunctionHeader(index), |
151 | 3.71k | bcProvider_->getBytecode(index), |
152 | 3.71k | index); |
153 | 3.71k | return functionMap_[index]; |
154 | 3.71k | } |
155 | | |
156 | | #ifndef HERMESVM_LEAN |
157 | | RuntimeModule *RuntimeModule::createLazyModule( |
158 | | Runtime &runtime, |
159 | | Handle<Domain> domain, |
160 | | RuntimeModule *parent, |
161 | 6 | uint32_t functionID) { |
162 | 6 | auto RM = createUninitialized(runtime, domain); |
163 | 6 | RM->lazyRoot_ = parent->lazyRoot_; |
164 | | // Copy the lazy root's script ID for lazy modules. |
165 | 6 | RM->scriptID_ = RM->lazyRoot_->scriptID_; |
166 | | |
167 | | // Set the bcProvider's BytecodeModule to point to the parent's. |
168 | 6 | assert(parent->isInitialized() && "Parent module must have been initialized"); |
169 | | |
170 | 6 | auto *bcFunction = &((hbc::BCProviderFromSrc *)parent->getBytecode()) |
171 | 6 | ->getBytecodeModule() |
172 | 6 | ->getFunction(functionID); |
173 | | |
174 | 6 | RM->bcProvider_ = hbc::BCProviderLazy::createBCProviderLazy(bcFunction); |
175 | | |
176 | | // We don't know which function index this block will eventually represent, |
177 | | // so just add it as 0 to ensure ownership. We'll move it later in |
178 | | // `initializeLazy`. |
179 | 6 | RM->functionMap_.emplace_back(CodeBlock::createCodeBlock( |
180 | 6 | RM, RM->bcProvider_->getFunctionHeader(functionID), {}, functionID)); |
181 | | |
182 | | // The module doesn't have a string table until we've compiled the block, |
183 | | // so just add the string name as 0 in the mean time for f.name to work via |
184 | | // getLazyName(). Since it's in the stringIDMap_, it'll be correctly GC'd. |
185 | 6 | RM->stringIDMap_.emplace_back(parent->getSymbolIDFromStringIDMayAllocate( |
186 | 6 | bcFunction->getHeader().functionName)); |
187 | | |
188 | 6 | return RM; |
189 | 6 | } |
190 | | |
191 | 6 | SymbolID RuntimeModule::getLazyName() { |
192 | 6 | assert(functionMap_.size() == 1 && "Not a lazy module?"); |
193 | 6 | assert(stringIDMap_.size() == 1 && "Missing lazy function name symbol"); |
194 | 6 | assert(this->stringIDMap_[0].isValid() && "Invalid function name symbol"); |
195 | 6 | return this->stringIDMap_[0]; |
196 | 6 | } |
197 | | |
198 | | void RuntimeModule::initializeLazyMayAllocate( |
199 | 0 | std::unique_ptr<hbc::BCProvider> bytecode) { |
200 | | // Clear the old data provider first. |
201 | 0 | bcProvider_ = nullptr; |
202 | | |
203 | | // Initialize without CJS module table because this compilation is done |
204 | | // separately, and the bytecode will not contain a module table. |
205 | 0 | initializeWithoutCJSModulesMayAllocate(std::move(bytecode)); |
206 | | |
207 | | // createLazyCodeBlock added a single codeblock as functionMap_[0] |
208 | 0 | assert(functionMap_[0] && "Missing first entry"); |
209 | | |
210 | | // We should move it to the index where it's supposed to be. This ensures a |
211 | | // 1-1 relationship between codeblocks and bytecodefunctions, which the |
212 | | // debugger relies on for setting step-out breakpoints in all functions. |
213 | 0 | if (bcProvider_->getGlobalFunctionIndex() == 0) { |
214 | | // No move needed |
215 | 0 | return; |
216 | 0 | } |
217 | 0 | assert( |
218 | 0 | !functionMap_[bcProvider_->getGlobalFunctionIndex()] && |
219 | 0 | "Entry point is already occupied"); |
220 | 0 | functionMap_[bcProvider_->getGlobalFunctionIndex()] = functionMap_[0]; |
221 | 0 | functionMap_[0] = nullptr; |
222 | 0 | } |
223 | | #endif |
224 | | |
225 | 256 | void RuntimeModule::importStringIDMapMayAllocate() { |
226 | 256 | assert(bcProvider_ && "Uninitialized RuntimeModule"); |
227 | 256 | PerfSection perf("Import String ID Map"); |
228 | 256 | GCScope scope(runtime_); |
229 | | |
230 | 256 | auto strTableSize = bcProvider_->getStringCount(); |
231 | | |
232 | 256 | stringIDMap_.clear(); |
233 | | |
234 | | // Populate the string ID map with empty identifiers. |
235 | 256 | stringIDMap_.resize(strTableSize, RootSymbolID(SymbolID::empty())); |
236 | | |
237 | 256 | if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsSequential) { |
238 | 0 | bcProvider_->adviseStringTableSequential(); |
239 | 0 | } |
240 | | |
241 | 256 | if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsWillNeed) { |
242 | 0 | bcProvider_->willNeedStringTable(); |
243 | 0 | } |
244 | | |
245 | | // Get the array of pre-computed hashes from identifiers in the bytecode |
246 | | // to their runtime representation as SymbolIDs. |
247 | 256 | auto kinds = bcProvider_->getStringKinds(); |
248 | 256 | auto hashes = bcProvider_->getIdentifierHashes(); |
249 | 256 | assert( |
250 | 256 | hashes.size() <= strTableSize && |
251 | 256 | "Should not have more strings than identifiers"); |
252 | | |
253 | | // Preallocate enough space to store all identifiers to prevent |
254 | | // unnecessary allocations. NOTE: If this module is not the first module, |
255 | | // then this is an underestimate. |
256 | 256 | runtime_.getIdentifierTable().reserve(hashes.size()); |
257 | 256 | { |
258 | 256 | StringID strID = 0; |
259 | 256 | uint32_t hashID = 0; |
260 | | |
261 | 321 | for (auto entry : kinds) { |
262 | 321 | switch (entry.kind()) { |
263 | 163 | case StringKind::String: |
264 | 163 | strID += entry.count(); |
265 | 163 | break; |
266 | | |
267 | 158 | case StringKind::Identifier: |
268 | 13.0k | for (uint32_t i = 0; i < entry.count(); ++i, ++strID, ++hashID) { |
269 | 12.8k | createSymbolFromStringIDMayAllocate( |
270 | 12.8k | strID, bcProvider_->getStringTableEntry(strID), hashes[hashID]); |
271 | 12.8k | } |
272 | 158 | break; |
273 | 321 | } |
274 | 321 | } |
275 | | |
276 | 256 | assert(strID == strTableSize && "Should map every string in the bytecode."); |
277 | 256 | assert(hashID == hashes.size() && "Should hash all identifiers."); |
278 | 256 | } |
279 | | |
280 | 256 | if (runtime_.getVMExperimentFlags() & experiments::MAdviseStringsRandom) { |
281 | 0 | bcProvider_->adviseStringTableRandom(); |
282 | 0 | } |
283 | | |
284 | 256 | if (strTableSize == 0) { |
285 | | // If the string table turns out to be empty, |
286 | | // we always add one empty string to it. |
287 | | // Note that this can only happen when we are creating the RuntimeModule |
288 | | // in a non-standard way, either in unit tests or the special |
289 | | // emptyCodeBlockRuntimeModule_ in Runtime where the creation happens |
290 | | // manually instead of going through bytecode module generation. |
291 | | // In those cases, functions will be created with a default nameID=0 |
292 | | // without adding the name string into the string table. Hence here |
293 | | // we need to add it manually and it will have index 0. |
294 | 93 | ASCIIRef s; |
295 | 93 | stringIDMap_.push_back({}); |
296 | 93 | mapStringMayAllocate(s, 0, hashString(s)); |
297 | 93 | } |
298 | 256 | } |
299 | | |
300 | 256 | void RuntimeModule::initializeFunctionMap() { |
301 | 256 | assert(bcProvider_ && "Uninitialized RuntimeModule"); |
302 | 256 | assert( |
303 | 256 | bcProvider_->getFunctionCount() >= functionMap_.size() && |
304 | 256 | "Unexpected size reduction. Lazy module missing functions?"); |
305 | 256 | functionMap_.resize(bcProvider_->getFunctionCount()); |
306 | 256 | } |
307 | | |
308 | 163 | ExecutionStatus RuntimeModule::importCJSModuleTable() { |
309 | 163 | PerfSection perf("Import CJS Module Table"); |
310 | 163 | return Domain::importCJSModuleTable(getDomain(runtime_), runtime_, this); |
311 | 163 | } |
312 | | |
313 | | StringPrimitive *RuntimeModule::getStringPrimFromStringIDMayAllocate( |
314 | 733k | StringID stringID) { |
315 | 733k | return runtime_.getStringPrimFromSymbolID( |
316 | 733k | getSymbolIDFromStringIDMayAllocate(stringID)); |
317 | 733k | } |
318 | | |
319 | 0 | std::string RuntimeModule::getStringFromStringID(StringID stringID) { |
320 | 0 | auto entry = bcProvider_->getStringTableEntry(stringID); |
321 | 0 | auto strStorage = bcProvider_->getStringStorage(); |
322 | 0 | if (entry.isUTF16()) { |
323 | 0 | const char16_t *s = |
324 | 0 | (const char16_t *)(strStorage.begin() + entry.getOffset()); |
325 | 0 | std::string out; |
326 | 0 | convertUTF16ToUTF8WithReplacements(out, UTF16Ref{s, entry.getLength()}); |
327 | 0 | return out; |
328 | 0 | } else { |
329 | | // ASCII. |
330 | 0 | const char *s = (const char *)strStorage.begin() + entry.getOffset(); |
331 | 0 | return std::string{s, entry.getLength()}; |
332 | 0 | } |
333 | 0 | } |
334 | | |
335 | | llvh::ArrayRef<uint8_t> RuntimeModule::getBigIntBytesFromBigIntId( |
336 | 620k | BigIntID bigIntId) const { |
337 | 620k | assert( |
338 | 620k | bigIntId < bcProvider_->getBigIntTable().size() && "Invalid bigint id"); |
339 | 620k | bigint::BigIntTableEntry entry = bcProvider_->getBigIntTable()[bigIntId]; |
340 | 620k | return bcProvider_->getBigIntStorage().slice(entry.offset, entry.length); |
341 | 620k | } |
342 | | |
343 | | llvh::ArrayRef<uint8_t> RuntimeModule::getRegExpBytecodeFromRegExpID( |
344 | 208 | uint32_t regExpId) const { |
345 | 208 | assert( |
346 | 208 | regExpId < bcProvider_->getRegExpTable().size() && "Invalid regexp id"); |
347 | 208 | RegExpTableEntry entry = bcProvider_->getRegExpTable()[regExpId]; |
348 | 208 | return bcProvider_->getRegExpStorage().slice(entry.offset, entry.length); |
349 | 208 | } |
350 | | |
351 | | template <typename T> |
352 | | SymbolID RuntimeModule::mapStringMayAllocate( |
353 | | llvh::ArrayRef<T> str, |
354 | | StringID stringID, |
355 | 13.7k | uint32_t hash) { |
356 | | // Create a SymbolID for a given string. In general a SymbolID holds onto an |
357 | | // intern'd StringPrimitive. As an optimization, if this RuntimeModule is |
358 | | // persistent, then it will not be deallocated before the Runtime, and we can |
359 | | // have the SymbolID hold a raw pointer into the storage and produce the |
360 | | // StringPrimitive when it is first required. |
361 | 13.7k | SymbolID id; |
362 | 13.7k | if (flags_.persistent) { |
363 | | // Registering a lazy identifier does not allocate, so we do not need a |
364 | | // GC scope. |
365 | 13.6k | id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash); |
366 | 13.6k | } else { |
367 | | // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope |
368 | | // marker. |
369 | 124 | GCScopeMarkerRAII scopeMarker{runtime_}; |
370 | 124 | id = *runtime_.ignoreAllocationFailure( |
371 | 124 | runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash)); |
372 | 124 | } |
373 | 13.7k | stringIDMap_[stringID] = RootSymbolID(id); |
374 | 13.7k | return id; |
375 | 13.7k | } hermes::vm::SymbolID hermes::vm::RuntimeModule::mapStringMayAllocate<char16_t>(llvh::ArrayRef<char16_t>, unsigned int, unsigned int) Line | Count | Source | 355 | 236 | uint32_t hash) { | 356 | | // Create a SymbolID for a given string. In general a SymbolID holds onto an | 357 | | // intern'd StringPrimitive. As an optimization, if this RuntimeModule is | 358 | | // persistent, then it will not be deallocated before the Runtime, and we can | 359 | | // have the SymbolID hold a raw pointer into the storage and produce the | 360 | | // StringPrimitive when it is first required. | 361 | 236 | SymbolID id; | 362 | 236 | if (flags_.persistent) { | 363 | | // Registering a lazy identifier does not allocate, so we do not need a | 364 | | // GC scope. | 365 | 234 | id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash); | 366 | 234 | } else { | 367 | | // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope | 368 | | // marker. | 369 | 2 | GCScopeMarkerRAII scopeMarker{runtime_}; | 370 | 2 | id = *runtime_.ignoreAllocationFailure( | 371 | 2 | runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash)); | 372 | 2 | } | 373 | 236 | stringIDMap_[stringID] = RootSymbolID(id); | 374 | 236 | return id; | 375 | 236 | } |
hermes::vm::SymbolID hermes::vm::RuntimeModule::mapStringMayAllocate<char>(llvh::ArrayRef<char>, unsigned int, unsigned int) Line | Count | Source | 355 | 13.5k | uint32_t hash) { | 356 | | // Create a SymbolID for a given string. In general a SymbolID holds onto an | 357 | | // intern'd StringPrimitive. As an optimization, if this RuntimeModule is | 358 | | // persistent, then it will not be deallocated before the Runtime, and we can | 359 | | // have the SymbolID hold a raw pointer into the storage and produce the | 360 | | // StringPrimitive when it is first required. | 361 | 13.5k | SymbolID id; | 362 | 13.5k | if (flags_.persistent) { | 363 | | // Registering a lazy identifier does not allocate, so we do not need a | 364 | | // GC scope. | 365 | 13.4k | id = runtime_.getIdentifierTable().registerLazyIdentifier(str, hash); | 366 | 13.4k | } else { | 367 | | // Accessing a symbol non-lazily may allocate in the GC heap, so add a scope | 368 | | // marker. | 369 | 122 | GCScopeMarkerRAII scopeMarker{runtime_}; | 370 | 122 | id = *runtime_.ignoreAllocationFailure( | 371 | 122 | runtime_.getIdentifierTable().getSymbolHandle(runtime_, str, hash)); | 372 | 122 | } | 373 | 13.5k | stringIDMap_[stringID] = RootSymbolID(id); | 374 | 13.5k | return id; | 375 | 13.5k | } |
|
376 | | |
377 | 582 | void RuntimeModule::markRoots(RootAcceptor &acceptor, bool markLongLived) { |
378 | 582 | for (auto &it : templateMap_) { |
379 | 128 | acceptor.acceptPtr(it.second); |
380 | 128 | } |
381 | | |
382 | 582 | if (markLongLived) { |
383 | 19.8k | for (auto symbol : stringIDMap_) { |
384 | 19.8k | if (symbol.isValid()) { |
385 | 13.0k | acceptor.accept(symbol); |
386 | 13.0k | } |
387 | 19.8k | } |
388 | 390 | } |
389 | 582 | } |
390 | | |
391 | 390 | void RuntimeModule::markLongLivedWeakRoots(WeakRootAcceptor &acceptor) { |
392 | 11.0k | for (auto &cbPtr : functionMap_) { |
393 | | // Only mark a CodeBlock is its non-null, and has not been scanned |
394 | | // previously in this top-level markRoots invocation. |
395 | 11.0k | if (cbPtr != nullptr && cbPtr->getRuntimeModule() == this) { |
396 | 5.20k | cbPtr->markCachedHiddenClasses(runtime_, acceptor); |
397 | 5.20k | } |
398 | 11.0k | } |
399 | 390 | for (auto &entry : objectLiteralHiddenClasses_) { |
400 | 0 | if (entry.second) { |
401 | 0 | acceptor.acceptWeak(entry.second); |
402 | 0 | } |
403 | 0 | } |
404 | 390 | } |
405 | | |
406 | | llvh::Optional<Handle<HiddenClass>> RuntimeModule::findCachedLiteralHiddenClass( |
407 | | Runtime &runtime, |
408 | | unsigned keyBufferIndex, |
409 | 0 | unsigned numLiterals) const { |
410 | 0 | if (canGenerateLiteralHiddenClassCacheKey(keyBufferIndex, numLiterals)) { |
411 | 0 | const auto cachedHiddenClassIter = objectLiteralHiddenClasses_.find( |
412 | 0 | getLiteralHiddenClassCacheHashKey(keyBufferIndex, numLiterals)); |
413 | 0 | if (cachedHiddenClassIter != objectLiteralHiddenClasses_.end()) { |
414 | 0 | if (HiddenClass *const cachedHiddenClass = |
415 | 0 | cachedHiddenClassIter->second.get(runtime, runtime.getHeap())) { |
416 | 0 | return runtime_.makeHandle(cachedHiddenClass); |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | 0 | return llvh::None; |
421 | 0 | } |
422 | | |
423 | | void RuntimeModule::tryCacheLiteralHiddenClass( |
424 | | Runtime &runtime, |
425 | | unsigned keyBufferIndex, |
426 | 0 | HiddenClass *clazz) { |
427 | 0 | auto numLiterals = clazz->getNumProperties(); |
428 | 0 | if (canGenerateLiteralHiddenClassCacheKey(keyBufferIndex, numLiterals)) { |
429 | 0 | assert( |
430 | 0 | !findCachedLiteralHiddenClass(runtime, keyBufferIndex, numLiterals) |
431 | 0 | .hasValue() && |
432 | 0 | "Why are we caching an item already cached?"); |
433 | 0 | objectLiteralHiddenClasses_[getLiteralHiddenClassCacheHashKey( |
434 | 0 | keyBufferIndex, numLiterals)] |
435 | 0 | .set(runtime, clazz); |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | 0 | size_t RuntimeModule::additionalMemorySize() const { |
440 | 0 | return stringIDMap_.capacity() * sizeof(SymbolID) + |
441 | 0 | objectLiteralHiddenClasses_.getMemorySize() + |
442 | 0 | templateMap_.getMemorySize(); |
443 | 0 | } |
444 | | |
445 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
446 | 0 | void RuntimeModule::snapshotAddNodes(GC &gc, HeapSnapshot &snap) const { |
447 | | // Create a native node for each CodeBlock owned by this module. |
448 | 0 | for (const CodeBlock *cb : functionMap_) { |
449 | | // Skip the null code blocks, they are lazily inserted the first time |
450 | | // they are used. |
451 | 0 | if (cb && cb->getRuntimeModule() == this) { |
452 | | // Only add a CodeBlock if this runtime module is the owner. |
453 | 0 | snap.beginNode(); |
454 | 0 | snap.endNode( |
455 | 0 | HeapSnapshot::NodeType::Native, |
456 | 0 | "CodeBlock", |
457 | 0 | gc.getNativeID(cb), |
458 | 0 | sizeof(CodeBlock) + cb->additionalMemorySize(), |
459 | 0 | 0); |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | | // Create a node for functionMap_. |
464 | 0 | snap.beginNode(); |
465 | | // Create an edge to each CodeBlock owned by this module. |
466 | 0 | for (int i = 0, e = functionMap_.size(); i < e; i++) { |
467 | 0 | const CodeBlock *cb = functionMap_[i]; |
468 | | // Skip the null code blocks, they are lazily inserted the first time |
469 | | // they are used. |
470 | 0 | if (cb && cb->getRuntimeModule() == this) { |
471 | | // Only add a CodeBlock if this runtime module is the owner. |
472 | 0 | snap.addIndexedEdge( |
473 | 0 | HeapSnapshot::EdgeType::Element, i, gc.getNativeID(cb)); |
474 | 0 | } |
475 | 0 | } |
476 | 0 | snap.endNode( |
477 | 0 | HeapSnapshot::NodeType::Native, |
478 | 0 | "std::vector<CodeBlock *>", |
479 | 0 | gc.getNativeID(&functionMap_), |
480 | 0 | functionMap_.capacity() * sizeof(CodeBlock *), |
481 | 0 | 0); |
482 | 0 | } |
483 | | |
484 | 0 | void RuntimeModule::snapshotAddEdges(GC &gc, HeapSnapshot &snap) const { |
485 | 0 | snap.addNamedEdge( |
486 | 0 | HeapSnapshot::EdgeType::Internal, |
487 | 0 | "functionMap", |
488 | 0 | gc.getNativeID(&functionMap_)); |
489 | 0 | } |
490 | | #endif // HERMES_MEMORY_INSTRUMENTATION |
491 | | |
492 | | namespace detail { |
493 | | |
494 | 0 | StringID mapStringMayAllocate(RuntimeModule &module, const char *str) { |
495 | 0 | module.stringIDMap_.push_back({}); |
496 | 0 | module.mapStringMayAllocate( |
497 | 0 | createASCIIRef(str), module.stringIDMap_.size() - 1); |
498 | 0 | return module.stringIDMap_.size() - 1; |
499 | 0 | } |
500 | | |
501 | | } // namespace detail |
502 | | |
503 | | } // namespace vm |
504 | | |
505 | | } // namespace hermes |