ArrayBuffer.h 13 KB

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