ArrayBuffer.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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 : public DynamicObject
  12. {
  13. public:
  14. // we need to install cross-site thunk on the nested array buffer when marshaling
  15. // typed array.
  16. DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBuffer, DynamicObject);
  17. virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext) = 0;
  18. #define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 //4GB
  19. private:
  20. void ClearParentsLength(ArrayBufferParent* parent);
  21. static uint32 GetByteLengthFromVar(ScriptContext* scriptContext, Var length);
  22. public:
  23. template <typename FreeFN>
  24. class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase
  25. {
  26. public:
  27. FreeFN* freeFunction;
  28. ArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength, FreeFN* freeFunction, ArrayBufferAllocationType allocationType)
  29. : ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, allocationType),
  30. freeFunction(freeFunction)
  31. {}
  32. virtual void ClearSelfOnly() override
  33. {
  34. HeapDelete(this);
  35. }
  36. virtual void DiscardState() override
  37. {
  38. if (this->buffer != nullptr)
  39. {
  40. freeFunction(this->buffer);
  41. this->buffer = nullptr;
  42. }
  43. this->bufferLength = 0;
  44. }
  45. virtual void Discard() override
  46. {
  47. ClearSelfOnly();
  48. }
  49. };
  50. template <typename Allocator>
  51. ArrayBuffer(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type, Allocator allocator);
  52. ArrayBuffer(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  53. class EntryInfo
  54. {
  55. public:
  56. static FunctionInfo NewInstance;
  57. static FunctionInfo Slice;
  58. static FunctionInfo IsView;
  59. static FunctionInfo GetterByteLength;
  60. static FunctionInfo GetterSymbolSpecies;
  61. static FunctionInfo Transfer;
  62. };
  63. static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
  64. static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
  65. static Var EntryIsView(RecyclableObject* function, CallInfo callInfo, ...);
  66. static Var EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...);
  67. static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);
  68. static Var EntryTransfer(RecyclableObject* function, CallInfo callInfo, ...);
  69. static bool Is(Var aValue);
  70. static ArrayBuffer* NewFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library);
  71. static ArrayBuffer* FromVar(Var aValue);
  72. virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  73. virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  74. virtual ArrayBufferDetachedStateBase* DetachAndGetState();
  75. bool IsDetached() { return this->isDetached; }
  76. void SetIsAsmJsBuffer(){ mIsAsmJsBuffer = true; }
  77. uint32 GetByteLength() const { return bufferLength; }
  78. BYTE* GetBuffer() const { return buffer; }
  79. static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); }
  80. static int GetIsDetachedOffset() { return offsetof(ArrayBuffer, isDetached); }
  81. static int GetBufferOffset() { return offsetof(ArrayBuffer, buffer); }
  82. void AddParent(ArrayBufferParent* parent);
  83. void RemoveParent(ArrayBufferParent* parent);
  84. #if _WIN64
  85. //maximum 2G -1 for amd64
  86. static const uint32 MaxArrayBufferLength = 0x7FFFFFFF;
  87. #else
  88. // maximum 1G to avoid arithmetic overflow.
  89. static const uint32 MaxArrayBufferLength = 1 << 30;
  90. #endif
  91. virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) { return false; }
  92. virtual bool IsValidVirtualBufferLength(uint length) { return false; }
  93. protected:
  94. typedef void __cdecl FreeFn(void* ptr);
  95. virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) = 0;
  96. virtual ArrayBuffer * TransferInternal(DECLSPEC_GUARD_OVERFLOW uint32 newBufferLength) = 0;
  97. static uint32 GetIndexFromVar(Js::Var arg, uint32 length, ScriptContext* scriptContext);
  98. //In most cases, the ArrayBuffer will only have one parent
  99. RecyclerWeakReference<ArrayBufferParent>* primaryParent;
  100. JsUtil::List<RecyclerWeakReference<ArrayBufferParent>*>* otherParents;
  101. BYTE *buffer; // Points to a heap allocated RGBA buffer, can be null
  102. uint32 bufferLength; // Number of bytes allocated
  103. // When an ArrayBuffer is detached, the TypedArray and DataView objects pointing to it must be made aware,
  104. // for this purpose the ArrayBuffer needs to hold WeakReferences to them
  105. bool isDetached;
  106. bool mIsAsmJsBuffer;
  107. bool isBufferCleared;
  108. };
  109. class ArrayBufferParent : public ArrayObject
  110. {
  111. friend ArrayBuffer;
  112. private:
  113. ArrayBuffer* arrayBuffer;
  114. protected:
  115. DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBufferParent, ArrayObject);
  116. ArrayBufferParent(DynamicType * type, uint32 length, ArrayBuffer* arrayBuffer)
  117. : ArrayObject(type, /*initSlots*/true, length),
  118. arrayBuffer(arrayBuffer)
  119. {
  120. arrayBuffer->AddParent(this);
  121. }
  122. void ClearArrayBuffer()
  123. {
  124. if (this->arrayBuffer != nullptr)
  125. {
  126. this->arrayBuffer->RemoveParent(this);
  127. this->arrayBuffer = nullptr;
  128. }
  129. }
  130. void SetArrayBuffer(ArrayBuffer* arrayBuffer)
  131. {
  132. this->ClearArrayBuffer();
  133. if (arrayBuffer != nullptr)
  134. {
  135. this->arrayBuffer->AddParent(this);
  136. this->arrayBuffer = arrayBuffer;
  137. }
  138. }
  139. public:
  140. ArrayBuffer* GetArrayBuffer() const
  141. {
  142. return this->arrayBuffer;
  143. }
  144. #if ENABLE_TTD
  145. public:
  146. virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
  147. virtual void ProcessCorePaths() override;
  148. #endif
  149. };
  150. // Normally we use malloc/free; for ArrayBuffer created from projection we need to use different allocator.
  151. class JavascriptArrayBuffer : public ArrayBuffer
  152. {
  153. protected:
  154. DEFINE_VTABLE_CTOR(JavascriptArrayBuffer, ArrayBuffer);
  155. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptArrayBuffer);
  156. public:
  157. static JavascriptArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  158. static JavascriptArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  159. virtual void Dispose(bool isShutdown) override;
  160. virtual void Finalize(bool isShutdown) override;
  161. static void*__cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length)
  162. {
  163. #if _WIN64
  164. LPVOID address = VirtualAlloc(nullptr, MAX_ASMJS_ARRAYBUFFER_LENGTH, MEM_RESERVE, PAGE_NOACCESS);
  165. //throw out of memory
  166. if (!address)
  167. {
  168. Js::Throw::OutOfMemory();
  169. }
  170. LPVOID arrayAddress = VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
  171. if (!arrayAddress)
  172. {
  173. VirtualFree(address, 0, MEM_RELEASE);
  174. Js::Throw::OutOfMemory();
  175. }
  176. return arrayAddress;
  177. #else
  178. Assert(false);
  179. return nullptr;
  180. #endif
  181. }
  182. static void FreeMemAlloc(Var ptr)
  183. {
  184. BOOL fSuccess = VirtualFree((LPVOID)ptr, 0, MEM_RELEASE);
  185. Assert(fSuccess);
  186. }
  187. virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) override;
  188. virtual bool IsValidVirtualBufferLength(uint length) override;
  189. protected:
  190. JavascriptArrayBuffer(DynamicType * type);
  191. virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
  192. virtual ArrayBuffer * TransferInternal(DECLSPEC_GUARD_OVERFLOW uint32 newBufferLength) override;
  193. private:
  194. JavascriptArrayBuffer(uint32 length, DynamicType * type);
  195. JavascriptArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
  196. #if ENABLE_TTD
  197. public:
  198. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  199. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  200. #endif
  201. };
  202. // the memory must be allocated via CoTaskMemAlloc.
  203. class ProjectionArrayBuffer : public ArrayBuffer
  204. {
  205. protected:
  206. DEFINE_VTABLE_CTOR(ProjectionArrayBuffer, ArrayBuffer);
  207. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ProjectionArrayBuffer);
  208. typedef void __stdcall FreeFn(LPVOID ptr);
  209. virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override
  210. {
  211. return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, CoTaskMemFree, ArrayBufferAllocationType::CoTask);
  212. }
  213. virtual ArrayBuffer * TransferInternal(DECLSPEC_GUARD_OVERFLOW uint32 newBufferLength) override;
  214. public:
  215. // Create constructor. script engine creates a buffer allocated via CoTaskMemAlloc.
  216. static ProjectionArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  217. // take over ownership. a CoTaskMemAlloc'ed buffer passed in via projection.
  218. static ProjectionArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
  219. virtual void Dispose(bool isShutdown) override;
  220. virtual void Finalize(bool isShutdown) override {};
  221. private:
  222. ProjectionArrayBuffer(uint32 length, DynamicType * type);
  223. ProjectionArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
  224. };
  225. // non-owning ArrayBuffer used for wrapping external data
  226. class ExternalArrayBuffer : public ArrayBuffer
  227. {
  228. protected:
  229. DEFINE_VTABLE_CTOR(ExternalArrayBuffer, ArrayBuffer);
  230. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ExternalArrayBuffer);
  231. public:
  232. ExternalArrayBuffer(byte *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
  233. protected:
  234. virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override { Assert(UNREACHED); Throw::InternalError(); };
  235. virtual ArrayBuffer * TransferInternal(DECLSPEC_GUARD_OVERFLOW uint32 newBufferLength) override { Assert(UNREACHED); Throw::InternalError(); };
  236. #if ENABLE_TTD
  237. public:
  238. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  239. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  240. #endif
  241. };
  242. }