| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- namespace Js
- {
- JavascriptWeakSet::JavascriptWeakSet(DynamicType* type)
- : DynamicObject(type),
- keySet(type->GetScriptContext()->GetRecycler())
- {
- }
- bool JavascriptWeakSet::Is(Var aValue)
- {
- return JavascriptOperators::GetTypeId(aValue) == TypeIds_WeakSet;
- }
- JavascriptWeakSet* JavascriptWeakSet::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptWeakSet'");
- return static_cast<JavascriptWeakSet *>(RecyclableObject::FromVar(aValue));
- }
- Var JavascriptWeakSet::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- JavascriptLibrary* library = scriptContext->GetLibrary();
- Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
- bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
- Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
- CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_WeakSet);
- JavascriptWeakSet* weakSetObject = nullptr;
- if (callInfo.Flags & CallFlags_New)
- {
- weakSetObject = library->CreateWeakSet();
- }
- else
- {
- JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet"), _u("WeakSet"));
- }
- Assert(weakSetObject != nullptr);
- Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined();
- RecyclableObject* iter = nullptr;
- RecyclableObject* adder = nullptr;
- if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext))
- {
- iter = JavascriptOperators::GetIterator(iterable, scriptContext);
- Var adderVar = JavascriptOperators::GetProperty(weakSetObject, PropertyIds::add, scriptContext);
- if (!JavascriptConversion::IsCallable(adderVar))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
- }
- adder = RecyclableObject::FromVar(adderVar);
- }
- if (iter != nullptr)
- {
- JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) {
- CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 2), weakSetObject, nextItem);
- });
- }
- #if ENABLE_TTD
- //TODO: right now we always GC before snapshots (assuming we have a weak collection)
- // may want to optimize this and use a notify here that we have a weak container -- also update post inflate and post snap
- #endif
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), weakSetObject, nullptr, scriptContext) :
- weakSetObject;
- }
- Var JavascriptWeakSet::EntryAdd(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- if (!JavascriptWeakSet::Is(args[0]))
- {
- JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.add"), _u("WeakSet"));
- }
- JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
- Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- if (!JavascriptOperators::IsObject(key) || JavascriptOperators::GetTypeId(key) == TypeIds_HostDispatch)
- {
- // HostDispatch is not expanded so can't have internal property added to it.
- // TODO: Support HostDispatch as WeakSet key
- JavascriptError::ThrowTypeError(scriptContext, JSERR_WeakMapSetKeyNotAnObject, _u("WeakSet.prototype.add"));
- }
- DynamicObject* keyObj = DynamicObject::FromVar(key);
- #if ENABLE_TTD
- //In replay we need to pin the object (and will release at snapshot points) -- in record we don't need to do anything
- if(scriptContext->IsTTDReplayModeEnabled())
- {
- scriptContext->TTDContextInfo->TTDWeakReferencePinSet->AddNew(keyObj);
- }
- #endif
- weakSet->Add(keyObj);
- return weakSet;
- }
- Var JavascriptWeakSet::EntryDelete(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- if (!JavascriptWeakSet::Is(args[0]))
- {
- JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.delete"), _u("WeakSet"));
- }
- JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
- Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- bool didDelete = false;
- if (JavascriptOperators::IsObject(key) && JavascriptOperators::GetTypeId(key) != TypeIds_HostDispatch)
- {
- DynamicObject* keyObj = DynamicObject::FromVar(key);
- didDelete = weakSet->Delete(keyObj);
- }
- #if ENABLE_TTD
- if(scriptContext->IsTTDRecordOrReplayModeEnabled())
- {
- if(scriptContext->IsTTDRecordModeEnabled())
- {
- function->GetScriptContext()->GetThreadContext()->TTDLog->RecordWeakCollectionContainsEvent(didDelete);
- }
- else
- {
- didDelete = function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayWeakCollectionContainsEvent();
- }
- }
- #endif
- return scriptContext->GetLibrary()->CreateBoolean(didDelete);
- }
- Var JavascriptWeakSet::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- if (!JavascriptWeakSet::Is(args[0]))
- {
- JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.has"), _u("WeakSet"));
- }
- JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
- Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- bool hasValue = false;
- if (JavascriptOperators::IsObject(key) && JavascriptOperators::GetTypeId(key) != TypeIds_HostDispatch)
- {
- DynamicObject* keyObj = DynamicObject::FromVar(key);
- hasValue = weakSet->Has(keyObj);
- }
- #if ENABLE_TTD
- if(scriptContext->IsTTDRecordOrReplayModeEnabled())
- {
- if(scriptContext->IsTTDRecordModeEnabled())
- {
- function->GetScriptContext()->GetThreadContext()->TTDLog->RecordWeakCollectionContainsEvent(hasValue);
- }
- else
- {
- hasValue = function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayWeakCollectionContainsEvent();
- }
- }
- #endif
- return scriptContext->GetLibrary()->CreateBoolean(hasValue);
- }
- void JavascriptWeakSet::Add(DynamicObject* key)
- {
- keySet.Item(key, true);
- }
- bool JavascriptWeakSet::Delete(DynamicObject* key)
- {
- bool unused = false;
- return keySet.TryGetValueAndRemove(key, &unused);
- }
- bool JavascriptWeakSet::Has(DynamicObject* key)
- {
- bool unused = false;
- return keySet.TryGetValue(key, &unused);
- }
- BOOL JavascriptWeakSet::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("WeakSet"));
- return TRUE;
- }
- #if ENABLE_TTD
- void JavascriptWeakSet::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
- {
- //All weak things should be reachable from another root so no need to mark but do need to repopulate the pin sets if in replay mode
- Js::ScriptContext* scriptContext = this->GetScriptContext();
- if(scriptContext->IsTTDReplayModeEnabled())
- {
- this->Map([&](DynamicObject* key)
- {
- scriptContext->TTDContextInfo->TTDWeakReferencePinSet->AddNew(key);
- });
- }
- }
- TTD::NSSnapObjects::SnapObjectType JavascriptWeakSet::GetSnapTag_TTD() const
- {
- return TTD::NSSnapObjects::SnapObjectType::SnapSetObject;
- }
- void JavascriptWeakSet::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
- {
- TTD::NSSnapObjects::SnapSetInfo* ssi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapSetInfo>();
- uint32 setCountEst = this->Size();
- ssi->SetSize = 0;
- ssi->SetValueArray = alloc.SlabReserveArraySpace<TTD::TTDVar>(setCountEst + 1); //always reserve at least 1 element
- this->Map([&](DynamicObject* key)
- {
- AssertMsg(ssi->SetSize < setCountEst, "We are writting junk");
- ssi->SetValueArray[ssi->SetSize] = key;
- ssi->SetSize++;
- });
- if(ssi->SetSize == 0)
- {
- ssi->SetValueArray = nullptr;
- alloc.SlabAbortArraySpace<TTD::TTDVar>(setCountEst + 1);
- }
- else
- {
- alloc.SlabCommitArraySpace<TTD::TTDVar>(ssi->SetSize, setCountEst + 1);
- }
- TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapSetInfo*, TTD::NSSnapObjects::SnapObjectType::SnapSetObject>(objData, ssi);
- }
- #endif
- }
|