blob: 2cd4726af102427a1ed3522c0dbbb166ead371e3 [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
{
RootObjectInlineCache::RootObjectInlineCache(InlineCacheAllocator * allocator):
inlineCache(nullptr), refCount(1)
{
inlineCache = AllocatorNewZ(InlineCacheAllocator,
allocator, Js::InlineCache);
}
RootObjectBase::RootObjectBase(DynamicType * type) :
DynamicObject(type), hostObject(nullptr), loadInlineCacheMap(nullptr), loadMethodInlineCacheMap(nullptr), storeInlineCacheMap(nullptr)
{}
RootObjectBase::RootObjectBase(DynamicType * type, ScriptContext* scriptContext) :
DynamicObject(type, scriptContext), hostObject(nullptr), loadInlineCacheMap(nullptr), loadMethodInlineCacheMap(nullptr), storeInlineCacheMap(nullptr)
{}
bool RootObjectBase::Is(Var var)
{
return RecyclableObject::Is(var) && RootObjectBase::Is(RecyclableObject::FromVar(var));
}
bool RootObjectBase::Is(RecyclableObject* obj)
{
TypeId id = obj->GetTypeId();
return id == TypeIds_GlobalObject || id == TypeIds_ModuleRoot;
}
RootObjectBase * RootObjectBase::FromVar(Var var)
{
Assert(RootObjectBase::Is(var));
return static_cast<Js::RootObjectBase *>(var);
}
HostObjectBase * RootObjectBase::GetHostObject() const
{
Assert(hostObject == nullptr || Js::JavascriptOperators::GetTypeId(hostObject) == TypeIds_HostObject);
return this->hostObject;
}
void RootObjectBase::SetHostObject(HostObjectBase * hostObject)
{
Assert(hostObject == nullptr || Js::JavascriptOperators::GetTypeId(hostObject) == TypeIds_HostObject);
this->hostObject = hostObject;
}
Js::InlineCache *
RootObjectBase::GetInlineCache(Js::PropertyRecord const* propertyRecord, bool isLoadMethod, bool isStore)
{
Js::RootObjectInlineCache * rootObjectInlineCache = GetRootInlineCache(propertyRecord, isLoadMethod, isStore);
return rootObjectInlineCache->GetInlineCache();
}
Js::RootObjectInlineCache*
RootObjectBase::GetRootInlineCache(Js::PropertyRecord const* propertyRecord, bool isLoadMethod, bool isStore)
{
RootObjectInlineCacheMap * inlineCacheMap = isStore ? storeInlineCacheMap :
isLoadMethod ? loadMethodInlineCacheMap : loadInlineCacheMap;
Js::RootObjectInlineCache * rootObjectInlineCache;
if (inlineCacheMap == nullptr)
{
Recycler * recycler = this->GetLibrary()->GetRecycler();
inlineCacheMap = RecyclerNew(recycler, RootObjectInlineCacheMap, recycler);
if (isStore)
{
this->storeInlineCacheMap = inlineCacheMap;
}
else if (isLoadMethod)
{
this->loadMethodInlineCacheMap = inlineCacheMap;
}
else
{
this->loadInlineCacheMap = inlineCacheMap;
}
}
else if (inlineCacheMap->TryGetValue(propertyRecord, &rootObjectInlineCache))
{
rootObjectInlineCache->AddRef();
return rootObjectInlineCache;
}
Recycler * recycler = this->GetLibrary()->GetRecycler();
rootObjectInlineCache = RecyclerNewLeaf(recycler, RootObjectInlineCache, this->GetScriptContext()->GetInlineCacheAllocator());
inlineCacheMap->Add(propertyRecord, rootObjectInlineCache);
return rootObjectInlineCache;
}
// TODO: Switch to take PropertyRecord instead once we clean up the function body to hold onto propertyRecord
// instead of propertyId.
void
RootObjectBase::ReleaseInlineCache(Js::PropertyId propertyId, bool isLoadMethod, bool isStore, bool isShutdown)
{
uint unregisteredInlineCacheCount = 0;
RootObjectInlineCacheMap * inlineCacheMap = isStore ? storeInlineCacheMap :
isLoadMethod ? loadMethodInlineCacheMap : loadInlineCacheMap;
bool found = false;
inlineCacheMap->RemoveIfWithKey(propertyId,
[this, isShutdown, &unregisteredInlineCacheCount, &found](PropertyRecord const * propertyRecord, RootObjectInlineCache * rootObjectInlineCache)
{
found = true;
if (rootObjectInlineCache->Release() == 0)
{
// If we're not shutting down, we need to remove this cache from thread context's invalidation list (if any),
// and release memory back to the arena. During script context shutdown, we leave everything in place, because
// the inline cache arena will stay alive until script context is destroyed (as in destructor called as opposed to
// Close called) and thus the invalidation lists are safe to keep references to caches from this script context.
if (!isShutdown)
{
if (rootObjectInlineCache->GetInlineCache()->RemoveFromInvalidationList())
{
unregisteredInlineCacheCount++;
}
AllocatorDelete(InlineCacheAllocator, this->GetScriptContext()->GetInlineCacheAllocator(), rootObjectInlineCache->GetInlineCache());
}
return true; // Remove from the map
}
return false; // don't remove from the map
}
);
Assert(found);
if (unregisteredInlineCacheCount > 0)
{
this->GetScriptContext()->GetThreadContext()->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount);
}
}
BOOL
RootObjectBase::EnsureProperty(PropertyId propertyId)
{
if (!RootObjectBase::HasOwnPropertyCheckNoRedecl(propertyId))
{
this->InitProperty(propertyId, this->GetLibrary()->GetUndefined(),
static_cast<Js::PropertyOperationFlags>(PropertyOperation_PreInit | PropertyOperation_SpecialValue));
}
return true;
}
BOOL
RootObjectBase::EnsureNoRedeclProperty(PropertyId propertyId)
{
RootObjectBase::HasOwnPropertyCheckNoRedecl(propertyId);
return true;
}
BOOL
RootObjectBase::HasOwnPropertyCheckNoRedecl(PropertyId propertyId)
{
bool noRedecl = false;
if (!GetTypeHandler()->HasRootProperty(this, propertyId, &noRedecl))
{
return FALSE;
}
else if (noRedecl)
{
JavascriptError::ThrowReferenceError(GetScriptContext(), ERRRedeclaration);
}
return true;
}
BOOL
RootObjectBase::HasRootProperty(PropertyId propertyId)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->HasRootProperty(this, propertyId);
}
BOOL
RootObjectBase::GetRootProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->GetRootProperty(this, originalInstance, propertyId, value, info, requestContext);
}
BOOL
RootObjectBase::GetRootPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->GetRootProperty(this, originalInstance, propertyId, value, info, requestContext);
}
BOOL
RootObjectBase::SetRootProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->SetRootProperty(this, propertyId, value, flags, info);
}
DescriptorFlags
RootObjectBase::GetRootSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->GetRootSetter(this, propertyId, setterValue, info, requestContext);
}
BOOL
RootObjectBase::DeleteRootProperty(PropertyId propertyId, PropertyOperationFlags flags)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->DeleteRootProperty(this, propertyId, flags);
}
PropertyIndex
RootObjectBase::GetRootPropertyIndex(PropertyId propertyId)
{
Assert(!Js::IsInternalPropertyId(propertyId));
Assert(propertyId != Constants::NoProperty);
return GetTypeHandler()->GetRootPropertyIndex(this->GetScriptContext()->GetPropertyName(propertyId));
}
void
RootObjectBase::EnsureNoProperty(PropertyId propertyId)
{
Assert(!Js::IsInternalPropertyId(propertyId));
bool isDeclared = false;
bool isNonconfigurable = false;
if (GetTypeHandler()->HasRootProperty(this, propertyId, nullptr, &isDeclared, &isNonconfigurable) &&
(isDeclared || isNonconfigurable))
{
JavascriptError::ThrowReferenceError(this->GetScriptContext(), ERRRedeclaration);
}
}
#if DBG
bool
RootObjectBase::IsLetConstGlobal(PropertyId propertyId)
{
Assert(!Js::IsInternalPropertyId(propertyId));
return GetTypeHandler()->IsLetConstGlobal(this, propertyId);
}
#endif
}