Hey folks, I have a class with many private functions/properties (77 in the most recent incarnation). When I get a stack trace (say to create an Error object) when I'm inside a function in this class, we end up in ClassScope::ClassScope in scopes.cc: ``` if (scope_info->HasSavedClassVariable()) { Tagged<String> name; int index; std::tie(name, index) = scope_info->SavedClassVariable(); ```
This ends up loading an index tacked on to the end of the context slots in the ScopeInfo heap object which looks like: ``` 0x25eabdda3541: [ScopeInfo] - map: 0x2ad145840d71 <Map(SCOPE_INFO_TYPE)> - parameters: 0 - context locals : 79 - local names in a hashtable: 0x25eabdda2d19 <Other heap object (NAME_TO_INDEX_HASH_TABLE_TYPE)> - scope type: CLASS_SCOPE - language mode: strict - class scope has private brand - has saved class variable - function kind: NormalFunction - outer scope info: 0x25eabdda2801 <ScopeInfo MODULE_SCOPE> - start position: 22541 - end position: 282321 - length: 87 - context slots { - 51: 0x1a45b21b8311 <String[17]: ##getTransactionId> - 72: 0x1a45b21b7fc9 <String[13]: ##valueMatches> - 0: 0x1a45b21b6d21 <String[17]: ##allowRunJsScript> - 32: 0x1a45b21b71d1 <String[5]: ##call> - 75: 0x1a45b21b9e59 <String[17]: ##writeNumericParm> - 77: 0x2ad145846ae1 <String[6]: #.brand> - 59: 0x1070007a8419 <String[13]: ##sendResponse> - 29: 0x1a45b21b70a1 <String[9]: ##timeZone> - 37: 0x1a45b21b84a9 <String[11]: ##createFind> - 71: 0x1a45b21bcc91 <String[14]: ##updateRecords> - 17: 0x1a45b21b6f69 <String[19]: ##pendingImageFields> - 28: 0x1a45b21b7081 <String[14]: ##syncSemaphore> - 27: 0x1a45b21b7061 <String[14]: ##syncQueueLock> - 39: 0x1a45b21b8a31 <String[5]: ##find> - 45: 0x1a45b21b91c1 <String[25]: ##getFindRecordTypeInfoSet> - 8: 0x1a45b21b6df9 <String[9]: ##inBuffer> - 44: 0x1a45b21b9d91 <String[12]: ##getFileInfo> - 66: 0x1a45b21bd609 <String[16]: ##syncBuildRecord> - 78: 0x208cca45af99 <String[10]: #Connection> ``` A lot more slots follow, but after them is the class variable info retrieved by the TorqueGeneratedScopeInfo::saved_class_variable_info function. FWIW, job in lldb does not show the value (23 in my example). That saved_class_variable_info is then used to index a name and value in the local names hash table referenced by the ScopeInfo heap object. And this table is used in ScopeInfo::SavedClassVariable to get the class name and index: ``` if (HasInlinedLocalNames()) { ... } else { // The saved class variable info corresponds to the offset in the hash // table storage. InternalIndex entry(saved_class_variable_info()); Tagged<NameToIndexHashTable> table = context_local_names_hashtable(); Tagged<Object> name = table->KeyAt(entry); DCHECK(IsString(name)); return std::make_pair(Cast<String>(name), table->IndexAt(entry)); } ``` The table is a hash table because it's gotten beyond a certain size. In my example it looks like: ``` 0x25eabdda2d19: [NameToIndexHashTable] - FixedArray length: 259 - elements: 79 - deleted: 0 - capacity: 12 - elements: { 0: #getTransactionId -> 51 1: #valueMatches -> 72 4: #allowRunJsScript -> 0 5: #call -> 32 6: #writeNumericParm -> 75 7: .brand -> 77 9: #sendResponse -> 59 10: #timeZone -> 29 11: #createFind -> 37 12: #updateRecords -> 71 13: #pendingImageFields -> 17 14: #syncSemaphore -> 28 15: #syncQueueLock -> 27 16: #find -> 39 17: #getFindRecordTypeInfoSet -> 45 18: #inBuffer -> 8 20: #getFileInfo -> 44 21: #syncBuildRecord -> 66 23: Connection -> 78 ``` As noted before, the saved_class_variable_info has value 23 which does, indeed, reference Connection, my class name. The problem occurs when I use the codeCache from the compilation on the Isolate shown above on a different Isolate. The ScopeInfo heap object looks largely identical to the first Isolate, but the local names hash table looks like it's been rehashed (unsurprisingly): ``` 0x27b95eb86f19: [NameToIndexHashTable] - FixedArray length: 259 - elements: 79 - deleted: 0 - capacity: 128 - elements: { 0: #getTransactionId -> 51 1: #valueMatches -> 72 4: #allowRunJsScript -> 0 5: #call -> 32 6: #writeNumericParm -> 75 7: .brand -> 77 9: #sendResponse -> 59 10: #timeZone -> 29 11: #createFind -> 37 12: #updateRecords -> 71 13: #pendingImageFields -> 17 14: #syncSemaphore -> 28 15: #syncQueueLock -> 27 16: #find -> 39 17: Connection -> 78 18: #inBuffer -> 8 19: #getFindRecordTypeInfoSet -> 45 20: #getUpdateValue -> 49 21: #syncBuildRecord -> 66 24: #syncImagineFileObj -> 24 ``` Note that the class name Connection is now in slot 17 and slot 23 is unused. So now, when we try to create the class ScopeInfo for a stack trace, the 23 in saved_class_variable_info references an unused slot and I get a seg fault or a failed DCHECK(isString(name)), depending on the type of build. Unfortunately, the fix seems beyond my meager skills. It seems that in deserialization for ScopeInfo, we would have to detect that we have a saved_class_variable_info and so need to recalculate saved_class_variable_info from the old value and the new hash table. But I'm not sure we can control the order in which the local names hash table and the ScopeInfo are deserialized and I'm not sure where in deserialization one would check for the ScopeInfo. Maybe someone more knowledgeable in the snapshot code could fix this relatively easily, suggest a work-around, or tell me what I'm doing wrong? FWIW, this problem occurs for me on V8 13.1.139 and 13.6.233.2. Thanks in advance! -- -- v8-users mailing list v8-users@googlegroups.com http://groups.google.com/group/v8-users --- You received this message because you are subscribed to the Google Groups "v8-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/v8-users/459a487d-1515-47e7-b9ee-55a1c99ce5a3n%40googlegroups.com.