| //------------------------------------------------------------------------------------------------------- |
| // 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 |
| } |