Var AtomicsObject::EntryWake(RecyclableObject* function, CallInfo callInfo, ...) { ATOMICS_FUNCTION_ENTRY_CHECKS(2, "Atomics.wake"); uint32 accessIndex = 0; TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext, true /*onlyInt32*/); int32 count = INT_MAX; if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3])) { double d = JavascriptConversion::ToInteger(args[3], scriptContext); if (!(NumberUtilities::IsNan(d) || JavascriptNumber::IsPosInf(d))) { int32 c = JavascriptConversion::ToInt32(d); count = max(0, c); } } Assert(typedArrayBase->GetBytesPerElement() == 4); uint32 bufferIndex = (accessIndex * 4) + typedArrayBase->GetByteOffset(); Assert(bufferIndex < typedArrayBase->GetArrayBuffer()->GetByteLength()); SharedArrayBuffer *sharedArrayBuffer = typedArrayBase->GetArrayBuffer()->GetAsSharedArrayBuffer(); WaiterList *waiterList = sharedArrayBuffer->GetWaiterList(bufferIndex); uint32 removed = 0; { AutoCriticalSection autoCS(waiterList->GetCriticalSectionForAccess()); removed = waiterList->RemoveAndWakeWaiters(count); } return JavascriptNumber::ToVar(removed, scriptContext); }
Var AtomicsObject::EntryWait(RecyclableObject* function, CallInfo callInfo, ...) { ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.wait"); uint32 accessIndex = 0; TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext, true /*onlyInt32*/); int32 value = JavascriptConversion::ToInt32(args[3], scriptContext); uint32 timeout = INFINITE; if (args.Info.Count > 4 && !JavascriptOperators::IsUndefinedObject(args[4])) { double t =JavascriptConversion::ToNumber(args[4], scriptContext); if (!(NumberUtilities::IsNan(t) || JavascriptNumber::IsPosInf(t))) { int32 t1 = JavascriptConversion::ToInt32(t); timeout = (uint32)max(0, t1); } } if (!AgentOfBuffer::AgentCanSuspend(scriptContext)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_CannotSuspendBuffer); } Assert(typedArrayBase->GetBytesPerElement() == 4); uint32 bufferIndex = (accessIndex * 4) + typedArrayBase->GetByteOffset(); Assert(bufferIndex < typedArrayBase->GetArrayBuffer()->GetByteLength()); SharedArrayBuffer *sharedArrayBuffer = typedArrayBase->GetArrayBuffer()->GetAsSharedArrayBuffer(); WaiterList *waiterList = sharedArrayBuffer->GetWaiterList(bufferIndex); bool awoken = false; { AutoCriticalSection autoCS(waiterList->GetCriticalSectionForAccess()); int32 w = JavascriptConversion::ToInt32(typedArrayBase->DirectGetItem(accessIndex), scriptContext); if (value != w) { return scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("not-equal")); } DWORD_PTR agent = (DWORD_PTR)scriptContext; Assert(sharedArrayBuffer->GetSharedContents()->IsValidAgent(agent)); awoken = waiterList->AddAndSuspendWaiter(agent, timeout); waiterList->RemoveWaiter(agent); } return awoken ? scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("ok")) : scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("timed-out")); }
// SharedArrayBuffer.prototype.byteLength Var SharedArrayBuffer::EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count == 0 || !SharedArrayBuffer::Is(args[0])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } SharedArrayBuffer* sharedArrayBuffer = SharedArrayBuffer::FromVar(args[0]); return JavascriptNumber::ToVar(sharedArrayBuffer->GetByteLength(), scriptContext); }
DetachedStateBase* SharedArrayBuffer::GetSharableState(Var object) { Assert(SharedArrayBuffer::Is(object)); SharedArrayBuffer * sab = SharedArrayBuffer::FromVar(object); SharedContents * contents = sab->GetSharedContents(); #if _WIN64 if (sab->IsValidVirtualBufferLength(contents->bufferLength)) { return HeapNew(SharableState, contents, ArrayBufferAllocationType::MemAlloc); } else { return HeapNew(SharableState, contents, ArrayBufferAllocationType::Heap); } #else return HeapNew(SharableState, contents, ArrayBufferAllocationType::Heap); #endif }
// SharedArrayBuffer.prototype.slice Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (!SharedArrayBuffer::Is(args[0])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } JavascriptLibrary* library = scriptContext->GetLibrary(); SharedArrayBuffer* currentBuffer = SharedArrayBuffer::FromVar(args[0]); int64 currentLen = (int64)currentBuffer->GetByteLength(); int64 start = 0, end = 0; int64 newLen = 0; // If no start or end arguments, use the entire length if (args.Info.Count < 2) { newLen = currentLen; } else { start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext); // If no end argument, use length as the end if (args.Info.Count < 3 || args[2] == library->GetUndefined()) { end = currentLen; } else { end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext); } newLen = end > start ? end - start : 0; } // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength. // Therefore, should be safe to cast down newLen to uint32. Assert(newLen < MaxSharedArrayBufferLength); uint32 newbyteLength = static_cast<uint32>(newLen); SharedArrayBuffer* newBuffer = nullptr; if (scriptContext->GetConfig()->IsES6SpeciesEnabled()) { Var constructorVar = JavascriptOperators::SpeciesConstructor(currentBuffer, scriptContext->GetLibrary()->GetSharedArrayBufferConstructor(), scriptContext); JavascriptFunction* constructor = JavascriptFunction::FromVar(constructorVar); Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); Js::Var newVar = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext); if (!SharedArrayBuffer::Is(newVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } newBuffer = SharedArrayBuffer::FromVar(newVar); if (newBuffer == currentBuffer) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } if (newBuffer->GetByteLength() < newbyteLength) { JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice")); } } else { newBuffer = library->CreateSharedArrayBuffer(newbyteLength); } Assert(newBuffer); Assert(newBuffer->GetByteLength() >= newbyteLength); // Don't bother doing memcpy if we aren't copying any elements if (newbyteLength > 0) { AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it"); js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength); } return newBuffer; }