JsrtSourceHolder.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. #include "JsrtPch.h"
  6. #include "JsrtSourceHolder.h"
  7. namespace Js
  8. {
  9. template <typename TLoadCallback>
  10. class JsrtSourceHolderPolicy
  11. {
  12. };
  13. #ifdef _WIN32 // JsSerializedScriptLoadSourceCallback is WIN32 only
  14. template <>
  15. class JsrtSourceHolderPolicy<JsSerializedScriptLoadSourceCallback>
  16. {
  17. public:
  18. typedef WCHAR TLoadCharType;
  19. // Helper function for converting a Unicode script to utf8.
  20. // If heapAlloc is true the returned buffer must be freed with HeapDelete.
  21. // Otherwise scriptContext must be provided and GCed object is
  22. // returned.
  23. static void ScriptToUtf8(_When_(heapAlloc, _In_opt_) _When_(!heapAlloc, _In_) Js::ScriptContext *scriptContext,
  24. _In_z_ const WCHAR *script, _Outptr_result_buffer_(*utf8Length) utf8char_t **utf8Script, _Out_ size_t *utf8Length,
  25. _Out_ size_t *scriptLength, _Out_opt_ size_t *utf8AllocLength, _In_ bool heapAlloc)
  26. {
  27. Assert(utf8Script != nullptr);
  28. Assert(utf8Length != nullptr);
  29. Assert(scriptLength != nullptr);
  30. *utf8Script = nullptr;
  31. *utf8Length = 0;
  32. *scriptLength = 0;
  33. if (utf8AllocLength != nullptr)
  34. {
  35. *utf8AllocLength = 0;
  36. }
  37. size_t length = wcslen(script);
  38. if (length > UINT_MAX)
  39. {
  40. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  41. }
  42. // `length` should not be bigger than MAXLONG
  43. // UINT_MAX / 3 < MAXLONG
  44. size_t cbUtf8Buffer = ((UINT_MAX / 3) - 1 > length) ? (length + 1) * 3 : UINT_MAX;
  45. if (cbUtf8Buffer >= UINT_MAX)
  46. {
  47. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  48. }
  49. if (!heapAlloc)
  50. {
  51. Assert(scriptContext != nullptr);
  52. *utf8Script = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), utf8char_t, cbUtf8Buffer);
  53. }
  54. else
  55. {
  56. *utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
  57. }
  58. *utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(length));
  59. *scriptLength = length;
  60. if (utf8AllocLength != nullptr)
  61. {
  62. *utf8AllocLength = cbUtf8Buffer;
  63. }
  64. }
  65. static void FreeMappedSource(utf8char_t const * source, size_t allocLength)
  66. {
  67. HeapDeleteArray(allocLength, source);
  68. }
  69. };
  70. #endif // _WIN32
  71. template <typename TLoadCallback, typename TUnloadCallback>
  72. void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::EnsureSource(MapRequestFor requestedFor, const WCHAR* reasonString)
  73. {
  74. if (this->mappedSource != nullptr)
  75. {
  76. return;
  77. }
  78. Assert(scriptLoadCallback != nullptr);
  79. Assert(this->mappedSource == nullptr);
  80. const typename JsrtSourceHolderPolicy<TLoadCallback>::TLoadCharType *source = nullptr;
  81. size_t sourceLength = 0;
  82. utf8char_t *utf8Source = nullptr;
  83. size_t utf8Length = 0;
  84. size_t utf8AllocLength = 0;
  85. if (!scriptLoadCallback(sourceContext, &source))
  86. {
  87. // Assume out of memory
  88. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  89. }
  90. JsrtSourceHolderPolicy<TLoadCallback>::ScriptToUtf8(nullptr, source, &utf8Source, &utf8Length, &sourceLength, &utf8AllocLength, true);
  91. this->mappedSource = utf8Source;
  92. this->mappedSourceByteLength = utf8Length;
  93. this->mappedAllocLength = utf8AllocLength;
  94. this->scriptLoadCallback = nullptr;
  95. #if ENABLE_DEBUG_CONFIG_OPTIONS
  96. AssertMsg(reasonString != nullptr, "Reason string for why we are mapping the source was not provided.");
  97. JS_ETW(EventWriteJSCRIPT_SOURCEMAPPING(reasonString, (ushort)requestedFor));
  98. #endif
  99. }
  100. template <typename TLoadCallback, typename TUnloadCallback>
  101. void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Finalize(bool isShutdown)
  102. {
  103. Unload();
  104. }
  105. template <typename TLoadCallback, typename TUnloadCallback>
  106. void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Unload()
  107. {
  108. if (scriptUnloadCallback == nullptr)
  109. {
  110. return;
  111. }
  112. scriptUnloadCallback(sourceContext);
  113. if (this->mappedSource != nullptr)
  114. {
  115. JsrtSourceHolderPolicy<TLoadCallback>::FreeMappedSource(
  116. this->mappedSource, this->mappedAllocLength);
  117. this->mappedSource = nullptr;
  118. }
  119. // Don't allow load or unload again after told to unload.
  120. scriptLoadCallback = nullptr;
  121. scriptUnloadCallback = nullptr;
  122. sourceContext = NULL;
  123. }
  124. #ifndef NTBUILD // ChakraCore Only
  125. template <>
  126. class JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>
  127. {
  128. public:
  129. typedef JsValueRef TLoadCharType;
  130. static void ScriptToUtf8(_When_(heapAlloc, _In_opt_) _When_(!heapAlloc, _In_) Js::ScriptContext *scriptContext,
  131. _In_z_ const byte *script_, bool isUtf8, size_t length, _Outptr_result_buffer_(*utf8Length) utf8char_t **utf8Script, _Out_ size_t *utf8Length,
  132. _Out_ size_t *scriptLength, _Out_opt_ size_t *utf8AllocLength, _In_ bool heapAlloc)
  133. {
  134. if (isUtf8)
  135. {
  136. *utf8Script = (utf8char_t*)script_;
  137. *utf8Length = length;
  138. *scriptLength = length; // xplat-todo: incorrect for utf8
  139. if (utf8AllocLength)
  140. {
  141. *utf8AllocLength = 0;
  142. }
  143. }
  144. else
  145. {
  146. const WCHAR *script = (const WCHAR*) script_;
  147. Assert(utf8Script != nullptr);
  148. Assert(utf8Length != nullptr);
  149. Assert(scriptLength != nullptr);
  150. *utf8Script = nullptr;
  151. *utf8Length = 0;
  152. *scriptLength = 0;
  153. if (utf8AllocLength != nullptr)
  154. {
  155. *utf8AllocLength = 0;
  156. }
  157. size_t script_length = wcslen(script);
  158. if (script_length > UINT_MAX)
  159. {
  160. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  161. }
  162. // `script_length` should not be bigger than MAXLONG
  163. // UINT_MAX / 3 < MAXLONG
  164. size_t cbUtf8Buffer = ((UINT_MAX / 3) - 1 > script_length) ? (script_length + 1) * 3 : UINT_MAX;
  165. if (cbUtf8Buffer >= UINT_MAX)
  166. {
  167. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  168. }
  169. if (!heapAlloc)
  170. {
  171. Assert(scriptContext != nullptr);
  172. *utf8Script = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), utf8char_t, cbUtf8Buffer);
  173. }
  174. else
  175. {
  176. *utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
  177. }
  178. *utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(script_length));
  179. *scriptLength = script_length;
  180. if (utf8AllocLength != nullptr)
  181. {
  182. *utf8AllocLength = cbUtf8Buffer;
  183. }
  184. }
  185. }
  186. static void FreeMappedSource(utf8char_t const * source, size_t allocLength)
  187. {
  188. if (allocLength)
  189. {
  190. HeapDeleteArray(allocLength, source);
  191. }
  192. }
  193. };
  194. template <>
  195. void JsrtSourceHolder<JsSerializedLoadScriptCallback,
  196. JsSerializedScriptUnloadCallback>::EnsureSource(MapRequestFor requestedFor,
  197. const WCHAR* reasonString)
  198. {
  199. if (this->mappedSource != nullptr)
  200. {
  201. return;
  202. }
  203. Assert(scriptLoadCallback != nullptr);
  204. Assert(this->mappedSource == nullptr);
  205. JsValueRef scriptVal;
  206. JsParseScriptAttributes attributes = JsParseScriptAttributeNone;
  207. size_t sourceLength = 0;
  208. utf8char_t *utf8Source = nullptr;
  209. size_t utf8Length = 0;
  210. size_t utf8AllocLength = 0;
  211. Js::ScriptContext* scriptContext = JsrtContext::GetCurrent()->GetScriptContext();
  212. BEGIN_LEAVE_SCRIPT(scriptContext)
  213. if (!scriptLoadCallback(sourceContext, &scriptVal, &attributes))
  214. {
  215. // Assume out of memory
  216. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  217. }
  218. END_LEAVE_SCRIPT(scriptContext);
  219. bool isExternalArray = Js::VarIs<Js::ArrayBuffer>(scriptVal),
  220. isString = false;
  221. bool isUtf8 = !(attributes & JsParseScriptAttributeArrayBufferIsUtf16Encoded);
  222. if (!isExternalArray)
  223. {
  224. isString = Js::VarIs<Js::JavascriptString>(scriptVal);
  225. if (!isString)
  226. {
  227. Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
  228. return;
  229. }
  230. }
  231. const byte* script = isExternalArray ?
  232. ((Js::ExternalArrayBuffer*)(scriptVal))->GetBuffer() :
  233. (const byte*)((Js::JavascriptString*)(scriptVal))->GetSz();
  234. const size_t cb = isExternalArray ?
  235. ((Js::ExternalArrayBuffer*)(scriptVal))->GetByteLength() :
  236. ((Js::JavascriptString*)(scriptVal))->GetLength();
  237. JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>::ScriptToUtf8(nullptr,
  238. script, isUtf8, cb, &utf8Source, &utf8Length, &sourceLength,
  239. &utf8AllocLength, true);
  240. this->mappedScriptValue = scriptVal;
  241. this->mappedSource = utf8Source;
  242. this->mappedSourceByteLength = utf8Length;
  243. this->mappedAllocLength = utf8AllocLength;
  244. this->scriptLoadCallback = nullptr;
  245. #if ENABLE_DEBUG_CONFIG_OPTIONS
  246. AssertMsg(reasonString != nullptr, "Reason string for why we are mapping the source was not provided.");
  247. JS_ETW(EventWriteJSCRIPT_SOURCEMAPPING(reasonString, (ushort)requestedFor));
  248. #endif
  249. }
  250. template <>
  251. void JsrtSourceHolder<JsSerializedLoadScriptCallback, JsSerializedScriptUnloadCallback>::Finalize(bool isShutdown)
  252. {
  253. if (this->mappedSource != nullptr)
  254. {
  255. JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>::FreeMappedSource(
  256. this->mappedSource, this->mappedAllocLength);
  257. this->mappedSource = nullptr;
  258. }
  259. this->mappedScriptValue = nullptr;
  260. // Don't allow load or unload again after told to unload.
  261. scriptLoadCallback = nullptr;
  262. scriptUnloadCallback = nullptr;
  263. sourceContext = NULL;
  264. }
  265. template class JsrtSourceHolder<JsSerializedLoadScriptCallback, JsSerializedScriptUnloadCallback>;
  266. #endif // NTBUILD
  267. #ifdef _WIN32
  268. template class JsrtSourceHolder<JsSerializedScriptLoadSourceCallback, JsSerializedScriptUnloadCallback>;
  269. #endif // _WIN32
  270. };