SharedArrayBuffer.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  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. namespace Js
  7. {
  8. #if DBG
  9. void SharedContents::AddAgent(DWORD_PTR agent)
  10. {
  11. if (allowedAgents == nullptr)
  12. {
  13. allowedAgents = HeapNew(SharableAgents, &HeapAllocator::Instance);
  14. }
  15. allowedAgents->Add(agent);
  16. }
  17. bool SharedContents::IsValidAgent(DWORD_PTR agent)
  18. {
  19. return allowedAgents != nullptr && allowedAgents->Contains(agent);
  20. }
  21. #endif
  22. void SharedContents::Cleanup()
  23. {
  24. Assert(refCount == 0);
  25. buffer = nullptr;
  26. bufferLength = 0;
  27. #if DBG
  28. if (allowedAgents != nullptr)
  29. {
  30. HeapDelete(allowedAgents);
  31. allowedAgents = nullptr;
  32. }
  33. #endif
  34. if (indexToWaiterList != nullptr)
  35. {
  36. indexToWaiterList->Map([](uint index, WaiterList *waiters) {
  37. if (waiters != nullptr)
  38. {
  39. waiters->Cleanup();
  40. HeapDelete(waiters);
  41. waiters = nullptr;
  42. }
  43. });
  44. HeapDelete(indexToWaiterList);
  45. indexToWaiterList = nullptr;
  46. }
  47. }
  48. Var SharedArrayBuffer::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  49. {
  50. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  51. ARGUMENTS(args, callInfo);
  52. ScriptContext* scriptContext = function->GetScriptContext();
  53. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  54. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  55. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  56. Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
  57. if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
  58. {
  59. JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("SharedArrayBuffer"));
  60. }
  61. uint32 byteLength = 0;
  62. if (args.Info.Count > 1)
  63. {
  64. byteLength = ArrayBuffer::ToIndex(args[1], JSERR_ArrayLengthConstructIncorrect, scriptContext, MaxSharedArrayBufferLength);
  65. }
  66. RecyclableObject* newArr = scriptContext->GetLibrary()->CreateSharedArrayBuffer(byteLength);
  67. return isCtorSuperCall ?
  68. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), newArr, nullptr, scriptContext) :
  69. newArr;
  70. }
  71. // SharedArrayBuffer.prototype.byteLength
  72. Var SharedArrayBuffer::EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...)
  73. {
  74. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  75. ARGUMENTS(args, callInfo);
  76. ScriptContext* scriptContext = function->GetScriptContext();
  77. Assert(!(callInfo.Flags & CallFlags_New));
  78. if (args.Info.Count == 0 || !SharedArrayBuffer::Is(args[0]))
  79. {
  80. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  81. }
  82. SharedArrayBuffer* sharedArrayBuffer = SharedArrayBuffer::FromVar(args[0]);
  83. return JavascriptNumber::ToVar(sharedArrayBuffer->GetByteLength(), scriptContext);
  84. }
  85. // SharedArrayBuffer.prototype.slice
  86. Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
  87. {
  88. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  89. ARGUMENTS(args, callInfo);
  90. ScriptContext* scriptContext = function->GetScriptContext();
  91. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  92. Assert(!(callInfo.Flags & CallFlags_New));
  93. if (!SharedArrayBuffer::Is(args[0]))
  94. {
  95. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  96. }
  97. JavascriptLibrary* library = scriptContext->GetLibrary();
  98. SharedArrayBuffer* currentBuffer = SharedArrayBuffer::FromVar(args[0]);
  99. int64 currentLen = (int64)currentBuffer->GetByteLength();
  100. int64 start = 0, end = 0;
  101. int64 newLen = 0;
  102. // If no start or end arguments, use the entire length
  103. if (args.Info.Count < 2)
  104. {
  105. newLen = currentLen;
  106. }
  107. else
  108. {
  109. start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext);
  110. // If no end argument, use length as the end
  111. if (args.Info.Count < 3 || args[2] == library->GetUndefined())
  112. {
  113. end = currentLen;
  114. }
  115. else
  116. {
  117. end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext);
  118. }
  119. newLen = end > start ? end - start : 0;
  120. }
  121. // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength.
  122. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength.
  123. // Therefore, should be safe to cast down newLen to uint32.
  124. Assert(newLen < MaxSharedArrayBufferLength);
  125. uint32 newbyteLength = static_cast<uint32>(newLen);
  126. SharedArrayBuffer* newBuffer = nullptr;
  127. if (scriptContext->GetConfig()->IsES6SpeciesEnabled())
  128. {
  129. Var constructorVar = JavascriptOperators::SpeciesConstructor(currentBuffer, scriptContext->GetLibrary()->GetSharedArrayBufferConstructor(), scriptContext);
  130. JavascriptFunction* constructor = JavascriptFunction::FromVar(constructorVar);
  131. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) };
  132. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  133. Js::Var newVar = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
  134. if (!SharedArrayBuffer::Is(newVar))
  135. {
  136. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  137. }
  138. newBuffer = SharedArrayBuffer::FromVar(newVar);
  139. if (newBuffer == currentBuffer)
  140. {
  141. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  142. }
  143. if (newBuffer->GetByteLength() < newbyteLength)
  144. {
  145. JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice"));
  146. }
  147. }
  148. else
  149. {
  150. newBuffer = library->CreateSharedArrayBuffer(newbyteLength);
  151. }
  152. Assert(newBuffer);
  153. Assert(newBuffer->GetByteLength() >= newbyteLength);
  154. // Don't bother doing memcpy if we aren't copying any elements
  155. if (newbyteLength > 0)
  156. {
  157. AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it");
  158. js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength);
  159. }
  160. return newBuffer;
  161. }
  162. Var SharedArrayBuffer::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  163. {
  164. ARGUMENTS(args, callInfo);
  165. Assert(args.Info.Count > 0);
  166. return args[0];
  167. }
  168. SharedArrayBuffer* SharedArrayBuffer::FromVar(Var aValue)
  169. {
  170. AssertMsg(Is(aValue), "var must be an SharedArrayBuffer");
  171. return static_cast<SharedArrayBuffer *>(RecyclableObject::FromVar(aValue));
  172. }
  173. bool SharedArrayBuffer::Is(Var aValue)
  174. {
  175. return JavascriptOperators::GetTypeId(aValue) == TypeIds_SharedArrayBuffer;
  176. }
  177. DetachedStateBase* SharedArrayBuffer::GetSharableState(Var object)
  178. {
  179. Assert(SharedArrayBuffer::Is(object));
  180. SharedArrayBuffer * sab = SharedArrayBuffer::FromVar(object);
  181. SharedContents * contents = sab->GetSharedContents();
  182. #if ENABLE_FAST_ARRAYBUFFER
  183. if (sab->IsValidVirtualBufferLength(contents->bufferLength))
  184. {
  185. return HeapNew(SharableState, contents, ArrayBufferAllocationType::MemAlloc);
  186. }
  187. else
  188. {
  189. return HeapNew(SharableState, contents, ArrayBufferAllocationType::Heap);
  190. }
  191. #else
  192. return HeapNew(SharableState, contents, ArrayBufferAllocationType::Heap);
  193. #endif
  194. }
  195. SharedArrayBuffer* SharedArrayBuffer::NewFromSharedState(DetachedStateBase* state, JavascriptLibrary *library)
  196. {
  197. Assert(state->GetTypeId() == TypeIds_SharedArrayBuffer);
  198. SharableState* sharableState = static_cast<SharableState *>(state);
  199. if (sharableState->allocationType == ArrayBufferAllocationType::Heap || sharableState->allocationType == ArrayBufferAllocationType::MemAlloc)
  200. {
  201. return library->CreateSharedArrayBuffer(sharableState->contents);
  202. }
  203. Assert(false);
  204. return nullptr;
  205. }
  206. template <class Allocator>
  207. SharedArrayBuffer::SharedArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) :
  208. ArrayBufferBase(type), sharedContents(nullptr)
  209. {
  210. BYTE * buffer = nullptr;
  211. if (length > MaxSharedArrayBufferLength)
  212. {
  213. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  214. }
  215. else if (length > 0)
  216. {
  217. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  218. if (recycler->ReportExternalMemoryAllocation(length))
  219. {
  220. buffer = (BYTE*)allocator(length);
  221. if (buffer == nullptr)
  222. {
  223. recycler->ReportExternalMemoryFree(length);
  224. }
  225. }
  226. if (buffer == nullptr)
  227. {
  228. recycler->CollectNow<CollectOnTypedArrayAllocation>();
  229. if (recycler->ReportExternalMemoryAllocation(length))
  230. {
  231. buffer = (BYTE*)allocator(length);
  232. if (buffer == nullptr)
  233. {
  234. recycler->ReportExternalMemoryFailure(length);
  235. }
  236. }
  237. else
  238. {
  239. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  240. }
  241. }
  242. if (buffer != nullptr)
  243. {
  244. ZeroMemory(buffer, length);
  245. sharedContents = HeapNew(SharedContents, buffer, length);
  246. if (sharedContents == nullptr)
  247. {
  248. recycler->ReportExternalMemoryFailure(length);
  249. // What else could we do?
  250. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  251. }
  252. #if DBG
  253. sharedContents->AddAgent((DWORD_PTR)GetScriptContext());
  254. #endif
  255. }
  256. }
  257. }
  258. SharedArrayBuffer::SharedArrayBuffer(SharedContents * contents, DynamicType * type) :
  259. ArrayBufferBase(type), sharedContents(contents)
  260. {
  261. if (sharedContents == nullptr || sharedContents->bufferLength > MaxSharedArrayBufferLength)
  262. {
  263. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  264. }
  265. InterlockedIncrement(&sharedContents->refCount);
  266. #if DBG
  267. sharedContents->AddAgent((DWORD_PTR)GetScriptContext());
  268. #endif
  269. }
  270. WaiterList *SharedArrayBuffer::GetWaiterList(uint index)
  271. {
  272. if (sharedContents != nullptr)
  273. {
  274. if (sharedContents->indexToWaiterList == nullptr)
  275. {
  276. sharedContents->indexToWaiterList = HeapNew(IndexToWaitersMap, &HeapAllocator::Instance);
  277. }
  278. WaiterList * waiters = nullptr;
  279. if (!sharedContents->indexToWaiterList->TryGetValue(index, &waiters))
  280. {
  281. waiters = HeapNew(WaiterList);
  282. sharedContents->indexToWaiterList->Add(index, waiters);
  283. }
  284. return waiters;
  285. }
  286. Assert(false);
  287. return nullptr;
  288. }
  289. uint32 SharedArrayBuffer::GetByteLength() const
  290. {
  291. return sharedContents != nullptr ? sharedContents->bufferLength : 0;
  292. }
  293. BYTE* SharedArrayBuffer::GetBuffer() const
  294. {
  295. return sharedContents != nullptr ? sharedContents->buffer : nullptr;
  296. }
  297. BOOL SharedArrayBuffer::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  298. {
  299. stringBuilder->AppendCppLiteral(_u("Object, (SharedArrayBuffer)"));
  300. return TRUE;
  301. }
  302. BOOL SharedArrayBuffer::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  303. {
  304. stringBuilder->AppendCppLiteral(_u("[object SharedArrayBuffer]"));
  305. return TRUE;
  306. }
  307. JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(uint32 length, DynamicType * type) :
  308. SharedArrayBuffer(length, type, (IsValidVirtualBufferLength(length)) ? AsmJsVirtualAllocator : malloc)
  309. {
  310. }
  311. JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(SharedContents *sharedContents, DynamicType * type) :
  312. SharedArrayBuffer(sharedContents, type)
  313. {
  314. }
  315. JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(uint32 length, DynamicType * type)
  316. {
  317. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  318. JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, length, type);
  319. Assert(result);
  320. recycler->AddExternalMemoryUsage(length);
  321. return result;
  322. }
  323. JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(SharedContents *sharedContents, DynamicType * type)
  324. {
  325. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  326. JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, sharedContents, type);
  327. Assert(result != nullptr);
  328. if (sharedContents != nullptr)
  329. {
  330. // REVIEW : why do we need to increase this? it is the same memory we are sharing.
  331. recycler->AddExternalMemoryUsage(sharedContents->bufferLength);
  332. }
  333. return result;
  334. }
  335. bool JavascriptSharedArrayBuffer::IsValidVirtualBufferLength(uint length)
  336. {
  337. #if ENABLE_FAST_ARRAYBUFFER
  338. /*
  339. 1. length >= 2^16
  340. 2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
  341. 3. length is a multiple of 4K
  342. */
  343. return (!PHASE_OFF1(Js::TypedArrayVirtualPhase) &&
  344. (length >= 0x10000) &&
  345. (((length & (~length + 1)) == length) ||
  346. (length >= 0x1000000 &&
  347. ((length & 0xFFFFFF) == 0)
  348. )
  349. ) &&
  350. ((length % AutoSystemInfo::PageSize) == 0)
  351. );
  352. #else
  353. return false;
  354. #endif
  355. }
  356. void JavascriptSharedArrayBuffer::Finalize(bool isShutdown)
  357. {
  358. if (sharedContents == nullptr)
  359. {
  360. return;
  361. }
  362. uint ref = InterlockedDecrement(&sharedContents->refCount);
  363. if (ref == 0)
  364. {
  365. #if ENABLE_FAST_ARRAYBUFFER
  366. //AsmJS Virtual Free
  367. //TOD - see if isBufferCleared need to be added for free too
  368. if (IsValidVirtualBufferLength(sharedContents->bufferLength) && !sharedContents->isBufferCleared)
  369. {
  370. FreeMemAlloc(sharedContents->buffer);
  371. sharedContents->isBufferCleared = true;
  372. }
  373. else
  374. {
  375. free(sharedContents->buffer);
  376. }
  377. #else
  378. free(sharedContents->buffer);
  379. #endif
  380. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  381. recycler->ReportExternalMemoryFree(sharedContents->bufferLength);
  382. sharedContents->Cleanup();
  383. HeapDelete(sharedContents);
  384. }
  385. sharedContents = nullptr;
  386. }
  387. void JavascriptSharedArrayBuffer::Dispose(bool isShutdown)
  388. {
  389. /* See JavascriptArrayBuffer::Finalize */
  390. }
  391. WaiterList::WaiterList()
  392. : m_waiters(nullptr)
  393. {
  394. InitWaiterList();
  395. }
  396. void WaiterList::InitWaiterList()
  397. {
  398. Assert(m_waiters == nullptr);
  399. m_waiters = HeapNew(Waiters, &HeapAllocator::Instance);
  400. }
  401. void WaiterList::Cleanup()
  402. {
  403. if (m_waiters != nullptr)
  404. {
  405. HeapDelete(m_waiters);
  406. m_waiters = nullptr;
  407. }
  408. }
  409. bool WaiterList::Contains(DWORD_PTR agent)
  410. {
  411. Assert(m_waiters != nullptr);
  412. for (int i = 0; i < m_waiters->Count(); i++)
  413. {
  414. if (m_waiters->Item(i).identity == agent)
  415. {
  416. return true;
  417. }
  418. }
  419. return false;
  420. }
  421. bool WaiterList::AddAndSuspendWaiter(DWORD_PTR waiter, uint32 timeout)
  422. {
  423. #ifdef _WIN32
  424. Assert(m_waiters != nullptr);
  425. Assert(waiter != NULL);
  426. Assert(!Contains(waiter));
  427. AgentOfBuffer agent(waiter, CreateEvent(NULL, TRUE, FALSE, NULL));
  428. m_waiters->Add(agent);
  429. csForAccess.Leave();
  430. DWORD result = WaitForSingleObject(agent.event, timeout);
  431. csForAccess.Enter();
  432. return result == WAIT_OBJECT_0;
  433. #else
  434. // TODO for xplat
  435. return false;
  436. #endif
  437. }
  438. void WaiterList::RemoveWaiter(DWORD_PTR waiter)
  439. {
  440. #ifdef _WIN32
  441. Assert(m_waiters != nullptr);
  442. for (int i = m_waiters->Count() - 1; i >= 0; i--)
  443. {
  444. if (m_waiters->Item(i).identity == waiter)
  445. {
  446. CloseHandle(m_waiters->Item(i).event);
  447. m_waiters->RemoveAt(i);
  448. return;
  449. }
  450. }
  451. Assert(false);
  452. #endif
  453. // TODO for xplat
  454. }
  455. uint32 WaiterList::RemoveAndWakeWaiters(int32 count)
  456. {
  457. Assert(m_waiters != nullptr);
  458. Assert(count >= 0);
  459. uint32 removed = 0;
  460. #ifdef _WIN32
  461. while (count > 0 && m_waiters->Count() > 0)
  462. {
  463. AgentOfBuffer agent = m_waiters->Item(0);
  464. m_waiters->RemoveAt(0);
  465. count--; removed++;
  466. SetEvent(agent.event);
  467. // This agent will be closed when their respective call to wait has returned
  468. }
  469. #endif
  470. return removed;
  471. }
  472. bool AgentOfBuffer::AgentCanSuspend(ScriptContext *scriptContext)
  473. {
  474. // Currently we are allowing every agent (including main page) to suspend. If we want only web-worker to suspend un-comment below code.
  475. // return scriptContext != nullptr && scriptContext->webWorkerId != Constants::NonWebWorkerContextId;
  476. return true;
  477. }
  478. }