blob: b26e2b1cca9e3c7b50ff8ad3f64c50edfa197cef [file]
//-------------------------------------------------------------------------------------------------------
// 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
{
JavascriptSet::JavascriptSet(DynamicType* type)
: DynamicObject(type)
{
}
JavascriptSet* JavascriptSet::New(ScriptContext* scriptContext)
{
JavascriptSet* set = scriptContext->GetLibrary()->CreateSet();
set->set = RecyclerNew(scriptContext->GetRecycler(), SetDataSet, scriptContext->GetRecycler());
return set;
}
bool JavascriptSet::Is(Var aValue)
{
return JavascriptOperators::GetTypeId(aValue) == TypeIds_Set;
}
JavascriptSet* JavascriptSet::FromVar(Var aValue)
{
AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptSet'");
return static_cast<JavascriptSet *>(RecyclableObject::FromVar(aValue));
}
JavascriptSet::SetDataList::Iterator JavascriptSet::GetIterator()
{
return list.GetIterator();
}
Var JavascriptSet::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
JavascriptLibrary* library = scriptContext->GetLibrary();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Set"));
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_Set);
JavascriptSet* setObject = nullptr;
if (callInfo.Flags & CallFlags_New)
{
setObject = library->CreateSet();
}
else
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set"), _u("Set"));
}
Assert(setObject != 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(setObject, PropertyIds::add, scriptContext);
if (!JavascriptConversion::IsCallable(adderVar))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
}
adder = RecyclableObject::FromVar(adderVar);
}
if (setObject->set != nullptr)
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_ObjectIsAlreadyInitialized, _u("Set"), _u("Set"));
}
setObject->set = RecyclerNew(scriptContext->GetRecycler(), SetDataSet, scriptContext->GetRecycler());
if (iter != nullptr)
{
JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) {
CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 2), setObject, nextItem);
});
}
return isCtorSuperCall ?
JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), setObject, nullptr, scriptContext) :
setObject;
}
Var JavascriptSet::EntryAdd(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.add"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
if (JavascriptNumber::Is(value) && JavascriptNumber::IsNegZero(JavascriptNumber::GetValue(value)))
{
// Normalize -0 to +0
value = JavascriptNumber::New(0.0, scriptContext);
}
set->Add(value);
return set;
}
Var JavascriptSet::EntryClear(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.clear"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
set->Clear();
return scriptContext->GetLibrary()->GetUndefined();
}
Var JavascriptSet::EntryDelete(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.delete"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
bool didDelete = set->Delete(value);
return scriptContext->GetLibrary()->CreateBoolean(didDelete);
}
Var JavascriptSet::EntryForEach(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Set.prototype.forEach"));
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.forEach"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Set.prototype.forEach"));
}
RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
Var thisArg = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
auto iterator = set->GetIterator();
while (iterator.Next())
{
Var value = iterator.Current();
CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, value, args[0]);
}
return scriptContext->GetLibrary()->GetUndefined();
}
Var JavascriptSet::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.has"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
bool hasValue = set->Has(value);
return scriptContext->GetLibrary()->CreateBoolean(hasValue);
}
Var JavascriptSet::EntrySizeGetter(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.size"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
int size = set->Size();
return JavascriptNumber::ToVar(size, scriptContext);
}
Var JavascriptSet::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.entries"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
return scriptContext->GetLibrary()->CreateSetIterator(set, JavascriptSetIteratorKind::KeyAndValue);
}
Var JavascriptSet::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (!JavascriptSet::Is(args[0]))
{
JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.values"), _u("Set"));
}
JavascriptSet* set = JavascriptSet::FromVar(args[0]);
return scriptContext->GetLibrary()->CreateSetIterator(set, JavascriptSetIteratorKind::Value);
}
Var JavascriptSet::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
{
ARGUMENTS(args, callInfo);
Assert(args.Info.Count > 0);
return args[0];
}
void JavascriptSet::Add(Var value)
{
if (!set->ContainsKey(value))
{
SetDataNode* node = list.Append(value, GetScriptContext()->GetRecycler());
set->Add(value, node);
}
}
void JavascriptSet::Clear()
{
// TODO: (Consider) Should we clear the set here and leave it as large as it has grown, or
// toss it away and create a new empty set, letting it grow as needed?
list.Clear();
set->Clear();
}
bool JavascriptSet::Delete(Var value)
{
if (set->ContainsKey(value))
{
SetDataNode* node = set->Item(value);
list.Remove(node);
return set->Remove(value);
}
return false;
}
bool JavascriptSet::Has(Var value)
{
return set->ContainsKey(value);
}
int JavascriptSet::Size()
{
return set->Count();
}
BOOL JavascriptSet::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
{
stringBuilder->AppendCppLiteral(_u("Set"));
return TRUE;
}
#if ENABLE_TTD
void JavascriptSet::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
{
auto iterator = this->GetIterator();
while(iterator.Next())
{
extractor->MarkVisitVar(iterator.Current());
}
}
TTD::NSSnapObjects::SnapObjectType JavascriptSet::GetSnapTag_TTD() const
{
return TTD::NSSnapObjects::SnapObjectType::SnapSetObject;
}
void JavascriptSet::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
{
TTD::NSSnapObjects::SnapSetInfo* ssi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapSetInfo>();
ssi->SetSize = 0;
if(this->Size() == 0)
{
ssi->SetValueArray = nullptr;
}
else
{
ssi->SetValueArray = alloc.SlabAllocateArray<TTD::TTDVar>(this->Size());
auto iter = this->GetIterator();
while(iter.Next())
{
ssi->SetValueArray[ssi->SetSize] = iter.Current();
ssi->SetSize++;
}
}
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapSetInfo*, TTD::NSSnapObjects::SnapObjectType::SnapSetObject>(objData, ssi);
}
JavascriptSet* JavascriptSet::CreateForSnapshotRestore(ScriptContext* ctx)
{
JavascriptSet* res = ctx->GetLibrary()->CreateSet();
res->set = RecyclerNew(ctx->GetRecycler(), SetDataSet, ctx->GetRecycler());
return res;
}
#endif
}