WebAssemblyMemory.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 "RuntimeLibraryPch.h"
  6. #ifdef ENABLE_WASM
  7. namespace Js
  8. {
  9. WebAssemblyMemory::WebAssemblyMemory(WebAssemblyArrayBuffer* buffer, uint32 initial, uint32 maximum, DynamicType * type) :
  10. DynamicObject(type),
  11. m_buffer(buffer),
  12. m_initial(initial),
  13. m_maximum(maximum)
  14. {
  15. }
  16. /* static */
  17. bool
  18. WebAssemblyMemory::Is(Var value)
  19. {
  20. return JavascriptOperators::GetTypeId(value) == TypeIds_WebAssemblyMemory;
  21. }
  22. /* static */
  23. WebAssemblyMemory *
  24. WebAssemblyMemory::FromVar(Var value)
  25. {
  26. Assert(WebAssemblyMemory::Is(value));
  27. return static_cast<WebAssemblyMemory*>(value);
  28. }
  29. Var
  30. WebAssemblyMemory::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  31. {
  32. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  33. ARGUMENTS(args, callInfo);
  34. ScriptContext* scriptContext = function->GetScriptContext();
  35. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  36. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  37. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  38. Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
  39. if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
  40. {
  41. JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("WebAssembly.Memory"));
  42. }
  43. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  44. {
  45. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("memoryDescriptor"));
  46. }
  47. DynamicObject * memoryDescriptor = JavascriptObject::FromVar(args[1]);
  48. Var initVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::initial, scriptContext);
  49. uint32 initial = WebAssembly::ToNonWrappingUint32(initVar, scriptContext);
  50. uint32 maximum = UINT_MAX;
  51. if (JavascriptOperators::OP_HasProperty(memoryDescriptor, PropertyIds::maximum, scriptContext))
  52. {
  53. Var maxVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::maximum, scriptContext);
  54. maximum = WebAssembly::ToNonWrappingUint32(maxVar, scriptContext);
  55. }
  56. return CreateMemoryObject(initial, maximum, scriptContext);
  57. }
  58. Var
  59. WebAssemblyMemory::EntryGrow(RecyclableObject* function, CallInfo callInfo, ...)
  60. {
  61. ScriptContext* scriptContext = function->GetScriptContext();
  62. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  63. ARGUMENTS(args, callInfo);
  64. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  65. Assert(!(callInfo.Flags & CallFlags_New));
  66. if (!WebAssemblyMemory::Is(args[0]))
  67. {
  68. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedMemoryObject);
  69. }
  70. WebAssemblyMemory* memory = WebAssemblyMemory::FromVar(args[0]);
  71. Assert(ArrayBuffer::Is(memory->m_buffer));
  72. if (memory->m_buffer->IsDetached())
  73. {
  74. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray);
  75. }
  76. Var deltaVar = scriptContext->GetLibrary()->GetUndefined();
  77. if (args.Info.Count >= 2)
  78. {
  79. deltaVar = args[1];
  80. }
  81. uint32 deltaPages = WebAssembly::ToNonWrappingUint32(deltaVar, scriptContext);
  82. int32 oldPageCount = memory->GrowInternal(deltaPages);
  83. if (oldPageCount == -1)
  84. {
  85. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
  86. }
  87. return JavascriptNumber::ToVar(oldPageCount, scriptContext);
  88. }
  89. int32
  90. WebAssemblyMemory::GrowInternal(uint32 deltaPages)
  91. {
  92. const uint64 deltaBytes = (uint64)deltaPages * WebAssembly::PageSize;
  93. if (deltaBytes > ArrayBuffer::MaxArrayBufferLength)
  94. {
  95. return -1;
  96. }
  97. const uint32 oldBytes = m_buffer->GetByteLength();
  98. const uint64 newBytesLong = deltaBytes + oldBytes;
  99. if (newBytesLong > ArrayBuffer::MaxArrayBufferLength)
  100. {
  101. return -1;
  102. }
  103. CompileAssert(ArrayBuffer::MaxArrayBufferLength <= UINT32_MAX);
  104. const uint32 newBytes = (uint32)newBytesLong;
  105. const uint32 oldPageCount = oldBytes / WebAssembly::PageSize;
  106. Assert(oldBytes % WebAssembly::PageSize == 0);
  107. if (deltaBytes == 0)
  108. {
  109. return (int32)oldPageCount;
  110. }
  111. const uint32 newPageCount = oldPageCount + deltaPages;
  112. if (newPageCount > m_maximum)
  113. {
  114. return -1;
  115. }
  116. WebAssemblyArrayBuffer * newBuffer = nullptr;
  117. JavascriptExceptionObject* caughtExceptionObject = nullptr;
  118. try
  119. {
  120. newBuffer = m_buffer->GrowMemory(newBytes);
  121. }
  122. catch (const JavascriptException& err)
  123. {
  124. caughtExceptionObject = err.GetAndClear();
  125. Assert(caughtExceptionObject && caughtExceptionObject == ThreadContext::GetContextForCurrentThread()->GetPendingOOMErrorObject());
  126. return -1;
  127. }
  128. AssertOrFailFast(newBuffer);
  129. m_buffer = newBuffer;
  130. CompileAssert(ArrayBuffer::MaxArrayBufferLength / WebAssembly::PageSize <= INT32_MAX);
  131. return (int32)oldPageCount;
  132. }
  133. int32
  134. WebAssemblyMemory::GrowHelper(WebAssemblyMemory * mem, uint32 deltaPages)
  135. {
  136. return mem->GrowInternal(deltaPages);
  137. }
  138. Var
  139. WebAssemblyMemory::EntryGetterBuffer(RecyclableObject* function, CallInfo callInfo, ...)
  140. {
  141. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  142. ARGUMENTS(args, callInfo);
  143. ScriptContext* scriptContext = function->GetScriptContext();
  144. Assert(!(callInfo.Flags & CallFlags_New));
  145. if (args.Info.Count == 0 || !WebAssemblyMemory::Is(args[0]))
  146. {
  147. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedMemoryObject);
  148. }
  149. WebAssemblyMemory* memory = WebAssemblyMemory::FromVar(args[0]);
  150. Assert(ArrayBuffer::Is(memory->m_buffer));
  151. return memory->m_buffer;
  152. }
  153. WebAssemblyMemory *
  154. WebAssemblyMemory::CreateMemoryObject(uint32 initial, uint32 maximum, ScriptContext * scriptContext)
  155. {
  156. uint32 byteLength = UInt32Math::Mul<WebAssembly::PageSize>(initial);
  157. WebAssemblyArrayBuffer* buffer = scriptContext->GetLibrary()->CreateWebAssemblyArrayBuffer(byteLength);
  158. return RecyclerNewFinalized(scriptContext->GetRecycler(), WebAssemblyMemory, buffer, initial, maximum, scriptContext->GetLibrary()->GetWebAssemblyMemoryType());
  159. }
  160. WebAssemblyArrayBuffer*
  161. WebAssemblyMemory::GetBuffer() const
  162. {
  163. return m_buffer;
  164. }
  165. uint
  166. WebAssemblyMemory::GetInitialLength() const
  167. {
  168. return m_initial;
  169. }
  170. uint
  171. WebAssemblyMemory::GetMaximumLength() const
  172. {
  173. return m_maximum;
  174. }
  175. } // namespace Js
  176. #endif // ENABLE_WASM