ArrayBuffer.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. // Implements ArrayBuffer according to Khronos spec.
  6. //----------------------------------------------------------------------------
  7. #pragma once
  8. namespace Js
  9. {
  10. class ArrayBufferParent;
  11. class ArrayBuffer;
  12. class SharedArrayBuffer;
  13. class ArrayBufferContentForDelayedFreeBase;
  14. class ArrayBufferBase : public DynamicObject
  15. {
  16. protected:
  17. #if ENABLE_FAST_ARRAYBUFFER
  18. #define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 // 4GB
  19. #define MAX_WASM__ARRAYBUFFER_LENGTH 0x200000000 // 8GB
  20. typedef void*(*AllocWrapperType)(size_t);
  21. #define AsmJsVirtualAllocator ((AllocWrapperType)Js::ArrayBuffer::AllocWrapper<MAX_ASMJS_ARRAYBUFFER_LENGTH>)
  22. #define WasmVirtualAllocator ((AllocWrapperType)Js::ArrayBuffer::AllocWrapper<MAX_WASM__ARRAYBUFFER_LENGTH>)
  23. #else
  24. #define AsmJsVirtualAllocator Js::ArrayBuffer::BadAllocCall
  25. #define WasmVirtualAllocator Js::ArrayBuffer::BadAllocCall
  26. static void* __cdecl BadAllocCall(DECLSPEC_GUARD_OVERFLOW size_t length)
  27. {
  28. // This allocator should never be used
  29. Js::Throw::FatalInternalError();
  30. }
  31. #endif
  32. #ifdef _WIN32
  33. static void* __cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length, size_t MaxVirtualSize)
  34. {
  35. LPVOID address = VirtualAlloc(nullptr, MaxVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
  36. //throw out of memory
  37. if (!address)
  38. {
  39. return nullptr;
  40. }
  41. if (length == 0)
  42. {
  43. return address;
  44. }
  45. LPVOID arrayAddress = VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
  46. if (!arrayAddress)
  47. {
  48. VirtualFree(address, 0, MEM_RELEASE);
  49. return nullptr;
  50. }
  51. return arrayAddress;
  52. }
  53. template<size_t MaxVirtualSize>
  54. static void* __cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length)
  55. {
  56. return AllocWrapper(length, MaxVirtualSize);
  57. }
  58. static void __cdecl FreeMemAlloc(Var ptr)
  59. {
  60. BOOL fSuccess = VirtualFree((LPVOID)ptr, 0, MEM_RELEASE);
  61. Assert(fSuccess);
  62. }
  63. #else
  64. static void __cdecl FreeMemAlloc(Var ptr)
  65. {
  66. // This free function should never be used
  67. Js::Throw::FatalInternalError();
  68. }
  69. #endif
  70. public:
  71. DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBufferBase, DynamicObject);
  72. virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext) = 0;
  73. ArrayBufferBase(DynamicType *type) :
  74. DynamicObject(type),
  75. isDetached(false),
  76. infoBits(0),
  77. externalized(false) { }
  78. bool IsDetached() { return isDetached; }
  79. #if ENABLE_TTD
  80. virtual void MarshalCrossSite_TTDInflate() = 0;
  81. #endif
  82. virtual bool IsArrayBuffer() = 0;
  83. virtual bool IsSharedArrayBuffer() = 0;
  84. virtual bool IsWebAssemblyArrayBuffer() { return false; }
  85. virtual ArrayBuffer * GetAsArrayBuffer() = 0;
  86. virtual SharedArrayBuffer * GetAsSharedArrayBuffer() { return nullptr; }
  87. virtual void AddParent(ArrayBufferParent* parent) { }
  88. virtual uint32 GetByteLength() const = 0;
  89. virtual BYTE* GetBuffer() const = 0;
  90. virtual bool IsValidVirtualBufferLength(uint length) const { return false; };
  91. char GetExtraInfoBits() { return infoBits; }
  92. void SetExtraInfoBits(char info) { infoBits = info; }
  93. static int GetIsDetachedOffset() { return offsetof(ArrayBufferBase, isDetached); }
  94. void Externalize() { this->externalized = true; }
  95. protected:
  96. Field(bool) isDetached;
  97. Field(bool) externalized;
  98. Field(char) infoBits;
  99. };
  100. template <> bool VarIsImpl<ArrayBufferBase>(RecyclableObject* obj);
  101. // This encapsulate buffer blob and the refCount.
  102. class RefCountedBuffer
  103. {
  104. private:
  105. FieldNoBarrier(BYTE*) buffer; // Points to a heap allocated RGBA buffer, can be null
  106. // Addref/release counter for current buffer, this is needed hold the current buffer alive
  107. Field(long) refCount;
  108. public:
  109. long AddRef();
  110. long Release();
  111. BYTE* GetBuffer() { return buffer; };
  112. long GetRefCount() { return refCount; }
  113. static int GetBufferOffset() { return offsetof(RefCountedBuffer, buffer); }
  114. RefCountedBuffer(BYTE* b)
  115. : buffer(b), refCount(1)
  116. { }
  117. };
  118. class ArrayBuffer : public ArrayBufferBase
  119. {
  120. public:
  121. // we need to install cross-site thunk on the nested array buffer when marshaling
  122. // typed array.
  123. DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBuffer, ArrayBufferBase);
  124. private:
  125. void DetachBufferFromParent(ArrayBufferParent* parent);
  126. public:
  127. template <typename FreeFN>
  128. class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase
  129. {
  130. public:
  131. FreeFN freeFunction;
  132. Recycler* recycler;
  133. ArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength, FreeFN freeFunction, Recycler* r, ArrayBufferAllocationType allocationType)
  134. : ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, allocationType),
  135. recycler(r),
  136. freeFunction(freeFunction)
  137. {}
  138. virtual void ClearSelfOnly() override
  139. {
  140. HeapDelete(this);
  141. }
  142. virtual void DiscardState() override
  143. {
  144. DiscardStateBase(freeFunction);
  145. }
  146. virtual void Discard() override
  147. {
  148. ClearSelfOnly();
  149. }
  150. };
  151. template <typename Allocator>
  152. ArrayBuffer(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type, Allocator allocator);
  153. ArrayBuffer(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type, bool isExternal = false);
  154. ArrayBuffer(RefCountedBuffer* buffContent, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  155. class EntryInfo
  156. {
  157. public:
  158. static FunctionInfo NewInstance;
  159. static FunctionInfo Slice;
  160. static FunctionInfo IsView;
  161. static FunctionInfo GetterByteLength;
  162. static FunctionInfo GetterSymbolSpecies;
  163. #if ENABLE_DEBUG_CONFIG_OPTIONS
  164. static FunctionInfo Detach;
  165. #endif
  166. };
  167. static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
  168. static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
  169. static Var EntryIsView(RecyclableObject* function, CallInfo callInfo, ...);
  170. static Var EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...);
  171. static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);
  172. #if ENABLE_DEBUG_CONFIG_OPTIONS
  173. static Var EntryDetach(RecyclableObject* function, CallInfo callInfo, ...);
  174. #endif
  175. static ArrayBuffer* NewFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library);
  176. virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  177. virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  178. ArrayBufferDetachedStateBase* DetachAndGetState(bool queueForDelayFree = true);
  179. virtual uint32 GetByteLength() const override;
  180. virtual BYTE* GetBuffer() const override;
  181. RefCountedBuffer *GetBufferContent() { return bufferContent; }
  182. static int GetBufferContentsOffset() { return offsetof(ArrayBuffer, bufferContent); }
  183. typedef void(__cdecl *FreeFn)(void*);
  184. virtual FreeFn GetArrayBufferFreeFn() { return nullptr; }
  185. static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); }
  186. virtual void AddParent(ArrayBufferParent* parent) override;
  187. void Detach();
  188. #if defined(TARGET_64)
  189. //maximum 2G -1 for amd64
  190. static const uint32 MaxArrayBufferLength = 0x7FFFFFFF;
  191. #else
  192. // maximum 1G to avoid arithmetic overflow.
  193. static const uint32 MaxArrayBufferLength = 1 << 30;
  194. #endif
  195. static const uint32 ParentsCleanupThreshold = 1000;
  196. virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) { return false; }
  197. virtual bool IsArrayBuffer() override { return true; }
  198. virtual bool IsSharedArrayBuffer() override { return false; }
  199. virtual ArrayBuffer * GetAsArrayBuffer() override;
  200. virtual ArrayBufferContentForDelayedFreeBase* CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength);
  201. static uint32 ToIndex(Var value, int32 errorCode, ScriptContext *scriptContext, uint32 MaxAllowedLength, bool checkSameValueZero = true);
  202. protected:
  203. virtual void ReportExternalMemoryFree();
  204. virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) = 0;
  205. // This function will be called from External buffer and projection buffer as they pass the buffer
  206. virtual void ReleaseBufferContent();
  207. //In most cases, the ArrayBuffer will only have one parent
  208. Field(RecyclerWeakReference<ArrayBufferParent>*) primaryParent;
  209. struct OtherParents :public SList<RecyclerWeakReference<ArrayBufferParent>*, Recycler>
  210. {
  211. OtherParents(Recycler* recycler)
  212. :SList<RecyclerWeakReference<ArrayBufferParent>*, Recycler>(recycler), increasedCount(0)
  213. {
  214. }
  215. Field(uint) increasedCount;
  216. };
  217. Field(OtherParents*) otherParents;
  218. FieldNoBarrier(RefCountedBuffer *) bufferContent;
  219. Field(uint32) bufferLength; // Number of bytes allocated
  220. };
  221. template <> inline bool VarIsImpl<ArrayBuffer>(RecyclableObject* obj)
  222. {
  223. return JavascriptOperators::GetTypeId(obj) == TypeIds_ArrayBuffer;
  224. }
  225. class ArrayBufferParent : public ArrayObject
  226. {
  227. friend ArrayBuffer;
  228. friend ArrayBufferBase;
  229. private:
  230. Field(ArrayBufferBase*) arrayBuffer;
  231. protected:
  232. DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBufferParent, ArrayObject);
  233. ArrayBufferParent(DynamicType * type, uint32 length, ArrayBufferBase* arrayBuffer)
  234. : ArrayObject(type, /*initSlots*/true, length),
  235. arrayBuffer(arrayBuffer)
  236. {
  237. arrayBuffer->AddParent(this);
  238. }
  239. public:
  240. ArrayBufferBase* GetArrayBuffer() const
  241. {
  242. return this->arrayBuffer;
  243. }
  244. #if ENABLE_TTD
  245. public:
  246. virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
  247. virtual void ProcessCorePaths() override;
  248. #endif
  249. };
  250. // Normally we use malloc/free; for ArrayBuffer created from projection we need to use different allocator.
  251. class JavascriptArrayBuffer : public ArrayBuffer
  252. {
  253. protected:
  254. DEFINE_VTABLE_CTOR(JavascriptArrayBuffer, ArrayBuffer);
  255. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptArrayBuffer);
  256. public:
  257. static JavascriptArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  258. static JavascriptArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  259. static JavascriptArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  260. virtual void Dispose(bool isShutdown) override;
  261. virtual void Finalize(bool isShutdown) override;
  262. static bool IsValidAsmJsBufferLengthAlgo(uint length, bool forceCheck);
  263. virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) override;
  264. virtual bool IsValidVirtualBufferLength(uint length) const override;
  265. virtual FreeFn GetArrayBufferFreeFn() override;
  266. protected:
  267. JavascriptArrayBuffer(DynamicType * type);
  268. virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
  269. template<typename Allocator>
  270. JavascriptArrayBuffer(uint32 length, DynamicType * type, Allocator allocator): ArrayBuffer(length, type, allocator){}
  271. JavascriptArrayBuffer(uint32 length, DynamicType * type);
  272. JavascriptArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
  273. JavascriptArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type);
  274. #if ENABLE_TTD
  275. public:
  276. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  277. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  278. #endif
  279. };
  280. #ifdef ENABLE_WASM
  281. class WebAssemblyArrayBuffer : public JavascriptArrayBuffer
  282. {
  283. template<typename Allocator>
  284. WebAssemblyArrayBuffer(uint32 length, DynamicType * type, Allocator allocator);
  285. WebAssemblyArrayBuffer(uint32 length, DynamicType * type);
  286. WebAssemblyArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
  287. protected:
  288. DEFINE_VTABLE_CTOR(WebAssemblyArrayBuffer, JavascriptArrayBuffer);
  289. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(WebAssemblyArrayBuffer);
  290. public:
  291. static WebAssemblyArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  292. WebAssemblyArrayBuffer* GrowMemory(uint32 newBufferLength);
  293. virtual bool IsValidVirtualBufferLength(uint length) const override;
  294. virtual bool IsWebAssemblyArrayBuffer() override { return true; }
  295. protected:
  296. virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
  297. };
  298. #endif
  299. // the memory must be allocated via CoTaskMemAlloc.
  300. class ProjectionArrayBuffer : public ArrayBuffer
  301. {
  302. protected:
  303. DEFINE_VTABLE_CTOR(ProjectionArrayBuffer, ArrayBuffer);
  304. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ProjectionArrayBuffer);
  305. typedef void (__stdcall *FreeFn)(LPVOID ptr);
  306. virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override
  307. {
  308. return HeapNew(ArrayBufferDetachedState<FreeFn>, content, bufferLength, CoTaskMemFree, GetScriptContext()->GetRecycler(), ArrayBufferAllocationType::CoTask);
  309. }
  310. public:
  311. // Create constructor. script engine creates a buffer allocated via CoTaskMemAlloc.
  312. static ProjectionArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  313. // take over ownership. a CoTaskMemAlloc'ed buffer passed in via projection.
  314. static ProjectionArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  315. static ProjectionArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  316. virtual ArrayBufferContentForDelayedFreeBase* CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
  317. virtual void Dispose(bool isShutdown) override;
  318. virtual void Finalize(bool isShutdown) override;
  319. private:
  320. ProjectionArrayBuffer(uint32 length, DynamicType * type);
  321. ProjectionArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
  322. ProjectionArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type);
  323. };
  324. // non-owning ArrayBuffer used for wrapping external data
  325. class ExternalArrayBuffer : public ArrayBuffer
  326. {
  327. protected:
  328. DEFINE_VTABLE_CTOR(ExternalArrayBuffer, ArrayBuffer);
  329. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ExternalArrayBuffer);
  330. public:
  331. ExternalArrayBuffer(byte *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
  332. ExternalArrayBuffer(RefCountedBuffer *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
  333. static ExternalArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  334. protected:
  335. virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
  336. virtual void ReportExternalMemoryFree() override;
  337. #if ENABLE_TTD
  338. public:
  339. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  340. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  341. #endif
  342. };
  343. class ExternalArrayBufferDetachedState : public ArrayBufferDetachedStateBase
  344. {
  345. public:
  346. ExternalArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength);
  347. virtual void ClearSelfOnly() override;
  348. virtual void DiscardState() override;
  349. virtual void Discard() override;
  350. virtual ArrayBuffer* Create(JavascriptLibrary* library);
  351. };
  352. }