Utf8SourceInfo.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace Js
  7. {
  8. struct Utf8SourceInfo : public FinalizableObject
  9. {
  10. typedef JsUtil::LeafValueDictionary<Js::LocalFunctionId, Js::FunctionBody*>::Type FunctionBodyDictionary;
  11. typedef JsUtil::LeafValueDictionary<Js::LocalFunctionId, Js::ParseableFunctionInfo*>::Type DeferredFunctionsDictionary;
  12. typedef JsUtil::List<Js::FunctionInfo *, Recycler> FunctionInfoList;
  13. typedef JsUtil::BaseHashSet<Js::PropertyRecord const *, Recycler> BoundedPropertyRecordHashSet;
  14. friend class RemoteUtf8SourceInfo;
  15. friend class ScriptContext;
  16. public:
  17. bool HasSource() const { return !this->sourceHolder->IsEmpty(); }
  18. LPCUTF8 GetSource(const char16 * reason = nullptr) const;
  19. size_t GetCbLength(const char16 * reason = nullptr) const;
  20. ULONG GetParseFlags()
  21. {
  22. return this->parseFlags;
  23. }
  24. void SetParseFlags(ULONG parseFlags)
  25. {
  26. this->parseFlags = parseFlags;
  27. }
  28. ULONG GetByteCodeGenerationFlags()
  29. {
  30. return this->byteCodeGenerationFlags;
  31. }
  32. void SetByteCodeGenerationFlags(ULONG byteCodeGenerationFlags)
  33. {
  34. this->byteCodeGenerationFlags = byteCodeGenerationFlags;
  35. }
  36. bool IsInDebugMode() const
  37. {
  38. #ifdef ENABLE_SCRIPT_DEBUGGING
  39. return (this->debugModeSource != nullptr || this->debugModeSourceIsEmpty) && this->m_isInDebugMode;
  40. #else
  41. return false;
  42. #endif
  43. }
  44. #ifdef ENABLE_SCRIPT_DEBUGGING
  45. void SetInDebugMode(bool inDebugMode)
  46. {
  47. AssertMsg(!GetIsLibraryCode(), "Shouldn't call SetInDebugMode for Library code.");
  48. AssertMsg(this->sourceHolder != nullptr, "We have no source holder.");
  49. AssertMsg(this->m_isInDebugMode != inDebugMode, "Why are we setting same value");
  50. this->m_isInDebugMode = inDebugMode;
  51. if (!this->sourceHolder->IsDeferrable())
  52. {
  53. return;
  54. }
  55. if (inDebugMode)
  56. {
  57. this->debugModeSource = this->sourceHolder->GetSource(_u("Entering Debug Mode"));
  58. this->debugModeSourceLength = this->sourceHolder->GetByteLength(_u("Entering Debug Mode"));
  59. this->debugModeSourceIsEmpty = !this->HasSource() || this->debugModeSource == nullptr;
  60. this->EnsureLineOffsetCache();
  61. }
  62. else
  63. {
  64. // If debugModeSourceIsEmpty is false, that means debugModeSource isn't nullptr or we aren't in debug mode.
  65. this->debugModeSourceIsEmpty = false;
  66. this->debugModeSource = nullptr;
  67. this->debugModeSourceLength = 0;
  68. }
  69. }
  70. #endif
  71. size_t CharacterIndexToByteIndex(charcount_t cchIndex) const
  72. {
  73. return cchIndex < m_cchLength ? (GetCbLength(_u("CharacterIndexToByteIndex")) == m_cchLength ? cchIndex : utf8::CharacterIndexToByteIndex(this->GetSource(_u("CharacterIndexToByteIndex")), GetCbLength(_u("CharacterIndexToByteIndex")), cchIndex, utf8::doAllowThreeByteSurrogates)) : m_cchLength;
  74. }
  75. charcount_t ByteIndexToCharacterIndex(size_t cbIndex) const
  76. {
  77. return cbIndex < GetCbLength(_u("CharacterIndexToByteIndex")) ? static_cast< charcount_t>(GetCbLength(_u("CharacterIndexToByteIndex")) == m_cchLength ? cbIndex : utf8::ByteIndexIntoCharacterIndex(this->GetSource(_u("CharacterIndexToByteIndex")), cbIndex, utf8::doAllowThreeByteSurrogates)) : static_cast< charcount_t >(GetCbLength(_u("CharacterIndexToByteIndex")));
  78. }
  79. charcount_t GetCchLength() const
  80. {
  81. return m_cchLength;
  82. }
  83. void SetCchLength(charcount_t cchLength)
  84. {
  85. m_cchLength = cchLength;
  86. }
  87. const SRCINFO* GetSrcInfo() const
  88. {
  89. return m_srcInfo;
  90. }
  91. void EnsureInitialized(int initialFunctionCount);
  92. // The following 3 functions are on individual functions that exist within this
  93. // source info- for them to be called, byte code generation should have created
  94. // the function body in question, in which case, functionBodyDictionary needs to have
  95. // been initialized
  96. void SetFunctionBody(FunctionBody * functionBody);
  97. void RemoveFunctionBody(FunctionBody* functionBodyBeingRemoved);
  98. void AddTopLevelFunctionInfo(Js::FunctionInfo * functionInfo, Recycler * recycler);
  99. void ClearTopLevelFunctionInfoList();
  100. JsUtil::List<Js::FunctionInfo *, Recycler> * EnsureTopLevelFunctionInfoList(Recycler * recycler);
  101. JsUtil::List<Js::FunctionInfo *, Recycler> * GetTopLevelFunctionInfoList() const
  102. {
  103. return this->topLevelFunctionInfoList;
  104. }
  105. // The following functions could get called even if EnsureInitialized hadn't gotten called
  106. // (Namely in the OOM scenario), so we simply guard against that condition rather than
  107. // asserting
  108. int GetFunctionBodyCount() const
  109. {
  110. return (this->functionBodyDictionary ? this->functionBodyDictionary->Count() : 0);
  111. }
  112. // Gets a thread-unique id for this source info
  113. // (Behaves the same as the function number)
  114. uint GetSourceInfoId()
  115. {
  116. return m_sourceInfoId;
  117. }
  118. #if ENABLE_TTD
  119. void SetSourceInfoForDebugReplay_TTD(uint32 newSourceInfoId)
  120. {
  121. this->m_sourceInfoId = newSourceInfoId;
  122. }
  123. #endif
  124. void ClearFunctions()
  125. {
  126. if (this->functionBodyDictionary)
  127. {
  128. this->functionBodyDictionary->Clear();
  129. }
  130. }
  131. bool HasFunctions() const
  132. {
  133. return (this->functionBodyDictionary ? this->functionBodyDictionary->Count() > 0 : false);
  134. }
  135. template <typename TDelegate>
  136. void MapFunction(TDelegate mapper) const
  137. {
  138. if (this->functionBodyDictionary)
  139. {
  140. this->functionBodyDictionary->Map([mapper] (Js::LocalFunctionId, Js::FunctionBody* functionBody) {
  141. Assert(functionBody);
  142. mapper(functionBody);
  143. });
  144. }
  145. }
  146. // Get's the first function in the function body dictionary
  147. // Used if the caller want's any function in this source info
  148. Js::FunctionBody* GetAnyParsedFunction();
  149. template <typename TDelegate>
  150. void MapFunctionUntil(TDelegate mapper) const
  151. {
  152. if (this->functionBodyDictionary)
  153. {
  154. this->functionBodyDictionary->MapUntil([mapper] (Js::LocalFunctionId, Js::FunctionBody* functionBody) {
  155. Assert(functionBody);
  156. return mapper(functionBody);
  157. });
  158. }
  159. }
  160. Js::FunctionBody* FindFunction(Js::LocalFunctionId id) const;
  161. template <typename TDelegate>
  162. Js::FunctionBody* FindFunction(TDelegate predicate) const
  163. {
  164. Js::FunctionBody* matchedFunctionBody = nullptr;
  165. // Function body collection could be null if we OOMed
  166. // during byte code generation but the source info didn't get
  167. // collected because of a false positive reference- we should
  168. // skip over these Utf8SourceInfos.
  169. if (this->functionBodyDictionary)
  170. {
  171. this->functionBodyDictionary->Map(
  172. [&matchedFunctionBody, predicate] (Js::LocalFunctionId, Js::FunctionBody* functionBody)
  173. {
  174. Assert(functionBody);
  175. if (predicate(functionBody)) {
  176. matchedFunctionBody = functionBody;
  177. return true;
  178. }
  179. return false;
  180. });
  181. }
  182. return matchedFunctionBody;
  183. }
  184. #ifdef ENABLE_SCRIPT_DEBUGGING
  185. bool HasDebugDocument() const
  186. {
  187. return m_debugDocument != nullptr;
  188. }
  189. void SetDebugDocument(DebugDocument * document)
  190. {
  191. Assert(!HasDebugDocument());
  192. m_debugDocument = document;
  193. }
  194. DebugDocument* GetDebugDocument() const
  195. {
  196. Assert(HasDebugDocument());
  197. return m_debugDocument;
  198. }
  199. void ClearDebugDocument(bool close = true);
  200. #endif
  201. void SetIsCesu8(bool isCesu8)
  202. {
  203. this->m_isCesu8 = isCesu8;
  204. }
  205. bool GetIsCesu8() const
  206. {
  207. return m_isCesu8;
  208. }
  209. DWORD_PTR GetSecondaryHostSourceContext() const
  210. {
  211. return m_secondaryHostSourceContext;
  212. }
  213. bool GetIsLibraryCode() const
  214. {
  215. return m_isLibraryCode;
  216. }
  217. bool GetIsXDomain() const { return m_isXDomain; }
  218. void SetIsXDomain() { m_isXDomain = true; }
  219. bool GetIsXDomainString() const { return m_isXDomainString; }
  220. void SetIsXDomainString() { m_isXDomainString = true; }
  221. DWORD_PTR GetHostSourceContext() const;
  222. bool IsDynamic() const;
  223. SourceContextInfo* GetSourceContextInfo() const;
  224. void SetSecondaryHostContext(DWORD_PTR hostSourceContext);
  225. bool IsHostManagedSource() const;
  226. static hash_t StaticGetHashCode(__in const Utf8SourceInfo* const si)
  227. {
  228. return si->GetSourceHolder()->GetHashCode();
  229. }
  230. static bool StaticEquals(__in Utf8SourceInfo* s1, __in Utf8SourceInfo* s2)
  231. {
  232. if (s1 == nullptr || s2 == nullptr) return false;
  233. //If the source holders have the same pointer, we are expecting the sources to be equal
  234. return (s1 == s2) || s1->GetSourceHolder()->Equals(s2->GetSourceHolder());
  235. }
  236. virtual void Finalize(bool isShutdown) override { /* nothing */ }
  237. virtual void Dispose(bool isShutdown) override;
  238. virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }
  239. static Utf8SourceInfo* NewWithHolder(ScriptContext* scriptContext,
  240. ISourceHolder* sourceHolder, int32 length, SRCINFO const* srcInfo,
  241. bool isLibraryCode, Js::Var scriptSource = nullptr);
  242. static Utf8SourceInfo* New(ScriptContext* scriptContext, LPCUTF8 utf8String,
  243. int32 length, size_t numBytes, SRCINFO const* srcInfo,
  244. bool isLibraryCode);
  245. static Utf8SourceInfo* NewWithNoCopy(ScriptContext* scriptContext,
  246. LPCUTF8 utf8String, int32 length, size_t numBytes,
  247. SRCINFO const* srcInfo, bool isLibraryCode, Js::Var scriptSource = nullptr);
  248. ScriptContext * GetScriptContext() const
  249. {
  250. return this->m_scriptContext;
  251. }
  252. void EnsureLineOffsetCache();
  253. HRESULT EnsureLineOffsetCacheNoThrow();
  254. void DeleteLineOffsetCache()
  255. {
  256. this->m_lineOffsetCache = nullptr;
  257. }
  258. void CreateLineOffsetCache(const charcount_t *lineCharacterOffsets, const charcount_t *lineByteOffsets, charcount_t numberOfItems);
  259. size_t GetLineCount()
  260. {
  261. return this->GetLineOffsetCache()->GetLineCount();
  262. }
  263. LineOffsetCache *GetLineOffsetCache()
  264. {
  265. AssertMsg(this->m_lineOffsetCache != nullptr, "LineOffsetCache wasn't created, EnsureLineOffsetCache should have been called.");
  266. return m_lineOffsetCache;
  267. }
  268. void GetLineInfoForCharPosition(charcount_t charPosition, charcount_t *outLineNumber, charcount_t *outColumn, charcount_t *outLineByteOffset, bool allowSlowLookup = false);
  269. void GetCharPositionForLineInfo(charcount_t lineNumber, charcount_t *outCharPosition, charcount_t *outByteOffset)
  270. {
  271. Assert(outCharPosition != nullptr && outByteOffset != nullptr);
  272. *outCharPosition = this->m_lineOffsetCache->GetCharacterOffsetForLine(lineNumber, outByteOffset);
  273. }
  274. void TrackDeferredFunction(Js::LocalFunctionId functionID, Js::ParseableFunctionInfo *function);
  275. void StopTrackingDeferredFunction(Js::LocalFunctionId functionID);
  276. template <class Fn>
  277. void UndeferGlobalFunctions(Fn fn)
  278. {
  279. if (this->m_scriptContext->DoUndeferGlobalFunctions())
  280. {
  281. Assert(m_deferredFunctionsInitialized);
  282. if (m_deferredFunctionsDictionary == nullptr)
  283. {
  284. return;
  285. }
  286. DeferredFunctionsDictionary *tmp = this->m_deferredFunctionsDictionary;
  287. this->m_deferredFunctionsDictionary = nullptr;
  288. tmp->MapAndRemoveIf(fn);
  289. }
  290. }
  291. ISourceHolder* GetSourceHolder() const
  292. {
  293. return sourceHolder;
  294. }
  295. bool IsCesu8() const
  296. {
  297. return this->m_isCesu8;
  298. }
  299. void SetCallerUtf8SourceInfo(Utf8SourceInfo* callerUtf8SourceInfo);
  300. Utf8SourceInfo* GetCallerUtf8SourceInfo() const;
  301. BoundedPropertyRecordHashSet * GetBoundedPropertyRecordHashSet() { return &this->boundedPropertyRecordHashSet; }
  302. #ifdef NTBUILD
  303. bool GetDebugDocumentName(BSTR * sourceName);
  304. #endif
  305. private:
  306. Field(charcount_t) m_cchLength; // The number of characters encoded in m_utf8Source.
  307. Field(ISourceHolder*) sourceHolder;
  308. Field(FunctionBodyDictionary*) functionBodyDictionary;
  309. Field(DeferredFunctionsDictionary*) m_deferredFunctionsDictionary;
  310. Field(FunctionInfoList*) topLevelFunctionInfoList;
  311. Field(BoundedPropertyRecordHashSet) boundedPropertyRecordHashSet;
  312. #ifdef ENABLE_SCRIPT_DEBUGGING
  313. Field(DebugDocument*) m_debugDocument;
  314. #endif
  315. Field(const SRCINFO*) m_srcInfo;
  316. Field(DWORD_PTR) m_secondaryHostSourceContext;
  317. #ifdef ENABLE_SCRIPT_DEBUGGING
  318. Field(LPCUTF8) debugModeSource;
  319. Field(size_t) debugModeSourceLength;
  320. #endif
  321. Field(ScriptContext* const) m_scriptContext; // Pointer to ScriptContext under which this source info was created
  322. // Line offset cache used for quickly finding line/column offsets.
  323. Field(LineOffsetCache*) m_lineOffsetCache;
  324. // Utf8SourceInfo of the caller, used for mapping eval/new Function node to its caller node for debugger
  325. Field(Utf8SourceInfo*) callerUtf8SourceInfo;
  326. Field(bool) m_deferredFunctionsInitialized : 1;
  327. Field(bool) m_isCesu8 : 1;
  328. Field(bool) m_isLibraryCode : 1; // true, the current source belongs to the internal library code. Used for debug purpose to not show in debugger
  329. Field(bool) m_isXDomain : 1;
  330. // we found that m_isXDomain could cause regression without CORS, so the new flag is just for callee.caller in window.onerror
  331. Field(bool) m_isXDomainString : 1;
  332. #ifdef ENABLE_SCRIPT_DEBUGGING
  333. Field(bool) debugModeSourceIsEmpty : 1;
  334. Field(bool) m_isInDebugMode : 1;
  335. #endif
  336. Field(uint) m_sourceInfoId;
  337. // Various flags preserved for Edit-and-Continue re-compile purpose
  338. Field(ULONG) parseFlags;
  339. Field(ULONG) byteCodeGenerationFlags;
  340. Utf8SourceInfo(ISourceHolder *sourceHolder, int32 cchLength, SRCINFO const* srcInfo,
  341. DWORD_PTR secondaryHostSourceContext, ScriptContext* scriptContext,
  342. bool isLibraryCode, Js::Var scriptSource = nullptr);
  343. #ifndef NTBUILD
  344. Field(Js::Var) sourceRef; // keep source string reference to prevent GC
  345. #endif
  346. };
  347. }
  348. template <>
  349. struct DefaultComparer<Js::Utf8SourceInfo*>
  350. {
  351. inline static bool Equals(Js::Utf8SourceInfo* const& x, Js::Utf8SourceInfo* const& y)
  352. {
  353. return Js::Utf8SourceInfo::StaticEquals(x, y);
  354. }
  355. inline static hash_t GetHashCode(Js::Utf8SourceInfo* const& s)
  356. {
  357. return Js::Utf8SourceInfo::StaticGetHashCode(s);
  358. }
  359. };