From 85de59f8d819f2c03564230425edf6cbfbc77fe4 Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Mon, 23 Oct 2017 16:40:10 -0700 Subject: [PATCH] Share types (i.e., allow PathTypeHandlers) for properties with non-standard attributes (non-writable, etc.). This change draws on Kount Veluri's work on behalf of native fields. It adds a wrinkle to the type path mechanism, replacing the PropertyId successor map key with a key that munges PropertyId and attributes. The way is paved for sharing types with getter/setters, deleted properties, and native values. When a property's attributes are changed/set, the type path is branched starting with the type that precedes the one that introduced the property. --- lib/Jsrt/JsrtExternalObject.cpp | 2 +- lib/Runtime/Debug/TTSnapObjects.cpp | 2 - lib/Runtime/Library/JavascriptLibrary.cpp | 50 +- lib/Runtime/Types/DynamicObject.h | 3 + lib/Runtime/Types/PathTypeHandler.cpp | 1331 +++++++++++++++++---- lib/Runtime/Types/PathTypeHandler.h | 361 +++++- lib/Runtime/Types/TypePath.h | 3 + 7 files changed, 1480 insertions(+), 272 deletions(-) diff --git a/lib/Jsrt/JsrtExternalObject.cpp b/lib/Jsrt/JsrtExternalObject.cpp index 1552d762bcb..b005cd08dab 100644 --- a/lib/Jsrt/JsrtExternalObject.cpp +++ b/lib/Jsrt/JsrtExternalObject.cpp @@ -13,7 +13,7 @@ JsrtExternalType::JsrtExternalType(Js::ScriptContext* scriptContext, JsFinalizeC Js::TypeIds_Object, scriptContext->GetLibrary()->GetObjectPrototype(), nullptr, - Js::SimplePathTypeHandler::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, 0, 0, true, true), + Js::SimplePathTypeHandlerNoAttr::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, 0, 0, true, true), true, true) , jsFinalizeCallback(finalizeCallback) diff --git a/lib/Runtime/Debug/TTSnapObjects.cpp b/lib/Runtime/Debug/TTSnapObjects.cpp index c5d74e5a698..af9482d9933 100644 --- a/lib/Runtime/Debug/TTSnapObjects.cpp +++ b/lib/Runtime/Debug/TTSnapObjects.cpp @@ -403,8 +403,6 @@ namespace TTD if(snpObject->SnapType->HasNoEnumerableProperties != obj->GetDynamicType()->GetHasNoEnumerableProperties()) { - TTDAssert(!obj->GetDynamicType()->GetIsShared(), "This is shared so we are mucking something up."); - obj->GetDynamicType()->SetHasNoEnumerableProperties(snpObject->SnapType->HasNoEnumerableProperties); } } diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index d3f5a93bec0..3099a924344 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -425,7 +425,7 @@ namespace Js #define INIT_SIMPLE_TYPE(field, typeId, prototype) \ field = DynamicType::New(scriptContext, typeId, prototype, nullptr, \ - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true) + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true) INIT_SIMPLE_TYPE(activationObjectType, TypeIds_ActivationObject, nullValue); INIT_SIMPLE_TYPE(arrayType, TypeIds_Array, arrayPrototype); @@ -523,7 +523,7 @@ namespace Js // Initialize Date types dateType = DynamicType::New(scriptContext, TypeIds_Date, datePrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); variantDateType = StaticType::New(scriptContext, TypeIds_VariantDate, nullValue, nullptr); anonymousFunctionTypeHandler = NullTypeHandler::GetDefaultInstance(); @@ -566,7 +566,7 @@ namespace Js else { boundFunctionType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, BoundFunction::NewInstance, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); } crossSiteDeferredPrototypeFunctionType = CreateDeferredPrototypeFunctionTypeNoProfileThunk( scriptContext->CurrentCrossSiteThunk, true /*isShared*/); @@ -627,8 +627,8 @@ namespace Js // Initialize Object types for (int16 i = 0; i < PreInitializedObjectTypeCount; i++) { - SimplePathTypeHandler * typeHandler = - SimplePathTypeHandler::New( + SimplePathTypeHandlerNoAttr * typeHandler = + SimplePathTypeHandlerNoAttr::New( scriptContext, this->GetRootPath(), 0, @@ -641,8 +641,8 @@ namespace Js } for (int16 i = 0; i < PreInitializedObjectTypeCount; i++) { - SimplePathTypeHandler * typeHandler = - SimplePathTypeHandler::New( + SimplePathTypeHandlerNoAttr * typeHandler = + SimplePathTypeHandlerNoAttr::New( scriptContext, this->GetRootPath(), 0, @@ -655,7 +655,7 @@ namespace Js DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, typeHandler, true, true); } - SimplePathTypeHandler * typeHandler = SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true); + SimplePathTypeHandlerNoAttr * typeHandler = SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true); nullPrototypeObjectType = DynamicType::New(scriptContext, TypeIds_Object, nullValue, nullptr, typeHandler, true, true); // Initialize regex types @@ -663,7 +663,7 @@ namespace Js regexResultPath->Add(BuiltInPropertyRecords::input); regexResultPath->Add(BuiltInPropertyRecords::index); regexResultType = DynamicType::New(scriptContext, TypeIds_Array, arrayPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, regexResultPath, regexResultPath->GetPathLength(), JavascriptRegularExpressionResult::InlineSlotCount, sizeof(JavascriptArray), true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, regexResultPath, regexResultPath->GetPathLength(), JavascriptRegularExpressionResult::InlineSlotCount, sizeof(JavascriptArray), true, true), true, true); // Initialize string types // static type is handled under StringCache.h @@ -673,33 +673,33 @@ namespace Js throwErrorObjectType = StaticType::New(scriptContext, TypeIds_Undefined, nullValue, ThrowErrorObject::DefaultEntryPoint); mapType = DynamicType::New(scriptContext, TypeIds_Map, mapPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); setType = DynamicType::New(scriptContext, TypeIds_Set, setPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); weakMapType = DynamicType::New(scriptContext, TypeIds_WeakMap, weakMapPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); weakSetType = DynamicType::New(scriptContext, TypeIds_WeakSet, weakSetPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); TypePath *const iteratorResultPath = TypePath::New(recycler); iteratorResultPath->Add(BuiltInPropertyRecords::value); iteratorResultPath->Add(BuiltInPropertyRecords::done); iteratorResultType = DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, iteratorResultPath, iteratorResultPath->GetPathLength(), 2, sizeof(DynamicObject), true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, iteratorResultPath, iteratorResultPath->GetPathLength(), 2, sizeof(DynamicObject), true, true), true, true); arrayIteratorType = DynamicType::New(scriptContext, TypeIds_ArrayIterator, arrayIteratorPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); mapIteratorType = DynamicType::New(scriptContext, TypeIds_MapIterator, mapIteratorPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); setIteratorType = DynamicType::New(scriptContext, TypeIds_SetIterator, setIteratorPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); stringIteratorType = DynamicType::New(scriptContext, TypeIds_StringIterator, stringIteratorPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); listIteratorType = DynamicType::New(scriptContext, TypeIds_ListIterator, iteratorPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); if (config->IsES6GeneratorsEnabled()) { @@ -711,10 +711,10 @@ namespace Js #ifdef ENABLE_DEBUG_CONFIG_OPTIONS debugDisposableObjectType = DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); debugFuncExecutorInDisposeObjectType = DynamicType::New(scriptContext, TypeIds_Object, objectPrototype, nullptr, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); #endif } @@ -2711,8 +2711,8 @@ namespace Js DeferredTypeHandler::GetDefaultInstance())); } - SimplePathTypeHandler *typeHandler = - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true); + SimplePathTypeHandlerNoAttr *typeHandler = + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true); // See JavascriptRegExp::IsWritable for property writability if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled()) { @@ -6860,7 +6860,7 @@ namespace Js } oldCachedType = dynamicType; #endif - SimplePathTypeHandler* typeHandler = SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, requestedInlineSlotCapacity, offsetOfInlineSlots, true, true); + SimplePathTypeHandlerNoAttr* typeHandler = SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, requestedInlineSlotCapacity, offsetOfInlineSlots, true, true); dynamicType = DynamicType::New(scriptContext, typeId, prototype, RecyclableObject::DefaultEntryPoint, typeHandler, true, true); if (useCache) @@ -6890,7 +6890,7 @@ namespace Js DynamicType* JavascriptLibrary::CreateObjectTypeNoCache(RecyclableObject* prototype, Js::TypeId typeId) { return DynamicType::New(scriptContext, typeId, prototype, RecyclableObject::DefaultEntryPoint, - SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); + SimplePathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); } DynamicType* JavascriptLibrary::CreateObjectType(RecyclableObject* prototype, uint16 requestedInlineSlotCapacity) diff --git a/lib/Runtime/Types/DynamicObject.h b/lib/Runtime/Types/DynamicObject.h index aa55120a8c8..15517c861d8 100644 --- a/lib/Runtime/Types/DynamicObject.h +++ b/lib/Runtime/Types/DynamicObject.h @@ -69,6 +69,9 @@ namespace Js friend class JavascriptNativeArray; // for xplat offsetof field access friend class JavascriptOperators; // for ReplaceType friend class PathTypeHandlerBase; // for ReplaceType + friend class SimplePathTypeHandlerNoAttr; + friend class SimplePathTypeHandlerWithAttr; + friend class PathTypeHandlerNoAttr; friend class JavascriptLibrary; // for ReplaceType friend class ScriptFunction; // for ReplaceType; friend class JSON::JSONParser; //for ReplaceType diff --git a/lib/Runtime/Types/PathTypeHandler.cpp b/lib/Runtime/Types/PathTypeHandler.cpp index 46c989546ba..3012232cb61 100644 --- a/lib/Runtime/Types/PathTypeHandler.cpp +++ b/lib/Runtime/Types/PathTypeHandler.cpp @@ -6,6 +6,58 @@ namespace Js { + // These attributes should match up for ease of translation + CompileAssert(ObjectSlotAttr_Enumerable == PropertyEnumerable); + CompileAssert(ObjectSlotAttr_Configurable == PropertyConfigurable); + CompileAssert(ObjectSlotAttr_Writable == PropertyWritable); + CompileAssert(ObjectSlotAttr_Deleted == PropertyDeleted); + + PathTypeSuccessorKey::PathTypeSuccessorKey() : propertyId(Constants::NoProperty), attributes(ObjectSlotAttr_Default) + { + } + + PathTypeSuccessorKey::PathTypeSuccessorKey( + const PropertyId propertyId, + const ObjectSlotAttributes attributes) + : propertyId(propertyId), attributes(attributes) + { + } + + bool PathTypeSuccessorKey::HasInfo() const + { + return propertyId != Constants::NoProperty; + } + + void PathTypeSuccessorKey::Clear() + { + propertyId = Constants::NoProperty; + } + + PropertyId PathTypeSuccessorKey::GetPropertyId() const + { + return propertyId; + } + + ObjectSlotAttributes PathTypeSuccessorKey::GetAttributes() const + { + return attributes; + } + + bool PathTypeSuccessorKey::operator ==(const PathTypeSuccessorKey &other) const + { + return propertyId == other.propertyId && attributes == other.attributes; + } + + bool PathTypeSuccessorKey::operator !=(const PathTypeSuccessorKey &other) const + { + return !(*this == other); + } + + hash_t PathTypeSuccessorKey::GetHashCode() const + { + return static_cast((propertyId << ObjectSlotAttr_BitSize) | static_cast(attributes)); + } + PathTypeHandlerBase::PathTypeHandlerBase(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : DynamicTypeHandler(slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, DefaultFlags | (isLocked ? IsLockedFlag : 0) | (isShared ? (MayBecomeSharedFlag | IsSharedFlag) : 0)), typePath(typePath), @@ -26,7 +78,7 @@ namespace Js { if (index < GetPathLength()) { - return typePath->GetPropertyId(index)->GetPropertyId(); + return GetTypePath()->GetPropertyId(index)->GetPropertyId(); } else { @@ -38,7 +90,7 @@ namespace Js { if (index < GetPathLength()) { - return typePath->GetPropertyId(index)->GetPropertyId(); + return GetTypePath()->GetPropertyId(index)->GetPropertyId(); } else { @@ -46,7 +98,7 @@ namespace Js } } - BOOL PathTypeHandlerBase::FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyStringName, PropertyId* propertyId, + BOOL PathTypeHandlerBase::FindNextPropertyHelper(ScriptContext* scriptContext, ObjectSlotAttributes * objectAttrs, PropertyIndex& index, JavascriptString** propertyStringName, PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) { Assert(propertyStringName); @@ -57,32 +109,43 @@ namespace Js { for (; index < GetPathLength(); ++index) { - const PropertyRecord* propertyRecord = typePath->GetPropertyId(index); - - // Skip this property if it is a symbol and we are not including symbol properties - if (!(flags & EnumeratorFlags::EnumSymbols) && propertyRecord->IsSymbol()) + ObjectSlotAttributes attr = objectAttrs ? objectAttrs[index] : ObjectSlotAttr_Default; + if( !(attr & ObjectSlotAttr_Deleted) && (!!(flags & EnumeratorFlags::EnumNonEnumerable) || (attr & ObjectSlotAttr_Enumerable))) { - continue; - } + const PropertyRecord* propertyRecord = GetTypePath()->GetPropertyId(index); - if (attributes) - { - *attributes = PropertyEnumerable; - } + // Skip this property if it is a symbol and we are not including symbol properties + if (!(flags & EnumeratorFlags::EnumSymbols) && propertyRecord->IsSymbol()) + { + continue; + } + + if (attributes) + { + *attributes = ObjectSlotAttributesToPropertyAttributes(attr); + } - *propertyId = propertyRecord->GetPropertyId(); - PropertyString* propertyString = scriptContext->GetPropertyString(*propertyId); - *propertyStringName = propertyString; + *propertyId = propertyRecord->GetPropertyId(); + PropertyString* propertyString = scriptContext->GetPropertyString(*propertyId); + *propertyStringName = propertyString; - PropertyValueInfo::SetCacheInfo(info, propertyString, propertyString->GetLdElemInlineCache(), false); - PropertyValueInfo::Set(info, instance, index); + if (attr & ObjectSlotAttr_Writable) + { + PropertyValueInfo::SetCacheInfo(info, propertyString, propertyString->GetLdElemInlineCache(), false); + PropertyValueInfo::Set(info, instance, index, ObjectSlotAttributesToPropertyAttributes(attr)); #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES - if (FixPropsOnPathTypes() && (index >= this->typePath->GetMaxInitializedLength() || this->typePath->GetIsFixedFieldAt(index, GetPathLength()))) - { - PropertyValueInfo::DisableStoreFieldCache(info); - } + if (FixPropsOnPathTypes() && (index >= this->GetTypePath()->GetMaxInitializedLength() || this->GetTypePath()->GetIsFixedFieldAt(index, GetPathLength()))) + { + PropertyValueInfo::DisableStoreFieldCache(info); + } #endif - return TRUE; + } + else + { + PropertyValueInfo::SetNoCache(info, instance); + } + return TRUE; + } } PropertyValueInfo::SetNoCache(info, instance); return FALSE; @@ -113,20 +176,130 @@ namespace Js return found; } + BOOL PathTypeHandlerBase::SetAttributesHelper(DynamicObject* instance, PropertyId propertyId, PropertyIndex propertyIndex, ObjectSlotAttributes * instanceAttributes, ObjectSlotAttributes propertyAttributes) + { + if (instanceAttributes == nullptr ? propertyAttributes == ObjectSlotAttr_Default : propertyAttributes == instanceAttributes[propertyIndex]) + { + return true; + } + + // Create a handler with attributes and use it to set the attribute. + + // Find the predecessor from which to branch. + PathTypeHandlerBase *predTypeHandler = this; + DynamicType *currentType = instance->GetDynamicType(); + while (predTypeHandler->GetPathLength() > propertyIndex) + { + currentType = predTypeHandler->GetPredecessorType(); + if (currentType == nullptr) + { + // This can happen if object header inlining is deoptimized, and we haven't built a full path from the root. + // For now, just punt this case. + return ConvertToSimpleDictionaryType(instance, GetPathLength())->SetAttributes(instance, propertyId, ObjectSlotAttributesToPropertyAttributes(propertyAttributes)); + } + predTypeHandler = PathTypeHandlerBase::FromTypeHandler(currentType->GetTypeHandler()); + } + Assert(predTypeHandler); + Assert(predTypeHandler->GetTypePath()->LookupInline(propertyId, predTypeHandler->GetPathLength()) == Constants::NoSlot); + + // Add this property with the new attributes and add the remaining properties with no attributes. + PropertyIndex pathLength = GetPathLength(); + PropertyIndex currentSlotIndex = propertyIndex; + ObjectSlotAttributes currentAttributes = propertyAttributes; + PathTypeHandlerBase *currentTypeHandler = predTypeHandler; + ScriptContext *scriptContext = instance->GetScriptContext(); + while (true) + { + const PropertyRecord *currentPropertyRecord = GetTypePath()->GetPropertyIdUnchecked(currentSlotIndex); + currentType = currentTypeHandler->PromoteType(currentType, PathTypeSuccessorKey(currentPropertyRecord->GetPropertyId(), currentAttributes), false, scriptContext, instance, ¤tSlotIndex); + currentTypeHandler = PathTypeHandlerBase::FromTypeHandler(currentType->GetTypeHandler()); +#if ENABLE_FIXED_FIELDS +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + currentTypeHandler->InitializePath( + instance, currentSlotIndex, currentTypeHandler->GetPathLength(), scriptContext, [=]() { return typePath->GetIsFixedFieldAt(currentSlotIndex, currentTypeHandler->GetPathLength()); }); +#endif +#endif + currentSlotIndex = currentTypeHandler->GetPathLength(); + if (currentSlotIndex >= pathLength) + { + break; + } + currentAttributes = instanceAttributes ? instanceAttributes[currentSlotIndex] : ObjectSlotAttr_Default; + } + + Assert(currentType != instance->GetType()); + instance->ReplaceType(currentType); + if(!IsolatePrototypes() && GetFlags() & IsPrototypeFlag) + { + scriptContext->InvalidateProtoCaches(propertyId); + } + + return true; + } + + +#if ENABLE_FIXED_FIELDS +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + void PathTypeHandlerBase::InitializeExistingPath( + const PropertyIndex slotIndex, + const PropertyIndex objectSlotCount, + ScriptContext *const scriptContext) + { + Assert(scriptContext); + + TypePath *const typePath = GetTypePath(); + Assert(slotIndex < typePath->GetMaxInitializedLength()); + Assert(objectSlotCount <= typePath->GetMaxInitializedLength()); + + if(typePath->GetIsUsedFixedFieldAt(slotIndex, objectSlotCount)) + { + // We are adding a new value where some other instance already has an existing value. If this is a fixed + // field we must clear the bit. If the value was hard coded in the JIT-ed code, we must invalidate the guards. + + // Invalidate any JIT-ed code that hard coded this method. No need to invalidate store field + // inline caches (which might quitely overwrite this fixed fields, because they have never been populated. + scriptContext->GetThreadContext()->InvalidatePropertyGuards(typePath->GetPropertyIdUnchecked(slotIndex)->GetPropertyId()); + } + + // If we're overwriting an existing value of this property, we don't consider the new one fixed. + // This also means that it's ok to populate the inline caches for this property from now on. + typePath->ClearIsFixedFieldAt(slotIndex, objectSlotCount); + + Assert(HasOnlyInitializedNonFixedProperties(/*typePath, objectSlotCount*/)); + Assert(HasSingletonInstanceOnlyIfNeeded(/*typePath*/)); + if(objectSlotCount == typePath->GetMaxInitializedLength()) + { + // We have now reached the most advanced instance along this path. If this instance is not the singleton instance, + // then the former singleton instance (if any) is no longer a singleton. This instance could be the singleton + // instance, if we just happen to set (overwrite) its last property. + + // This is perhaps the most fragile point of fixed fields on path types. If we cleared the singleton instance + // while some fields remained fixed, the instance would be collectible, and yet some code would expect to see + // values and call methods on it. Clearly, a recipe for disaster. We rely on the fact that we always add + // properties to (pre-initialized) type handlers in the order they appear on the type path. By the time + // we reach the singleton instance, all fixed fields will have been invalidated. Otherwise, some fields + // could remain fixed (or even uninitialized) and we would have to spin off a loop here to invalidate any + // remaining fixed fields - a rather unfortunate overhead. + typePath->ClearSingletonInstance(); + } + } +#endif +#endif + PropertyIndex PathTypeHandlerBase::GetPropertyIndex(const PropertyRecord* propertyRecord) { - return typePath->LookupInline(propertyRecord->GetPropertyId(), GetPathLength()); + return GetTypePath()->LookupInline(propertyRecord->GetPropertyId(), GetPathLength()); } PropertyIndex PathTypeHandlerBase::GetPropertyIndex(PropertyId propertyId) { - return typePath->LookupInline(propertyId, GetPathLength()); + return GetTypePath()->LookupInline(propertyId, GetPathLength()); } #if ENABLE_NATIVE_CODEGEN bool PathTypeHandlerBase::GetPropertyEquivalenceInfo(PropertyRecord const* propertyRecord, PropertyEquivalenceInfo& info) { - Js::PropertyIndex absSlotIndex = typePath->LookupInline(propertyRecord->GetPropertyId(), GetPathLength()); + Js::PropertyIndex absSlotIndex = GetTypePath()->LookupInline(propertyRecord->GetPropertyId(), GetPathLength()); info.slotIndex = AdjustSlotIndexForInlineSlots(absSlotIndex); info.isAuxSlot = absSlotIndex >= this->inlineSlotCapacity; info.isWritable = info.slotIndex != Constants::NoSlot; @@ -134,13 +307,18 @@ namespace Js } bool PathTypeHandlerBase::IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) + { + return IsObjTypeSpecEquivalentHelper(type, nullptr, record, failedPropertyIndex); + } + + bool PathTypeHandlerBase::IsObjTypeSpecEquivalentHelper(const Type* type, const ObjectSlotAttributes * attributes, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) { uint propertyCount = record.propertyCount; Js::EquivalentPropertyEntry* properties = record.properties; for (uint pi = 0; pi < propertyCount; pi++) { const EquivalentPropertyEntry* entry = &properties[pi]; - if (!this->PathTypeHandlerBase::IsObjTypeSpecEquivalent(type, entry)) + if (!this->PathTypeHandlerBase::IsObjTypeSpecEquivalentHelper(type, attributes, entry)) { failedPropertyIndex = pi; return false; @@ -152,23 +330,48 @@ namespace Js bool PathTypeHandlerBase::IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry *entry) { - Js::PropertyIndex absSlotIndex = typePath->LookupInline(entry->propertyId, GetPathLength()); + return IsObjTypeSpecEquivalentHelper(type, nullptr, entry); + } + + bool PathTypeHandlerBase::IsObjTypeSpecEquivalentHelper(const Type* type, const ObjectSlotAttributes * attributes, const EquivalentPropertyEntry *entry) + { + Js::PropertyIndex absSlotIndex = GetTypePath()->LookupInline(entry->propertyId, GetPathLength()); if (absSlotIndex != Constants::NoSlot) { - Js::PropertyIndex relSlotIndex = AdjustValidSlotIndexForInlineSlots(absSlotIndex); - if (relSlotIndex != entry->slotIndex || ((absSlotIndex >= GetInlineSlotCapacity()) != entry->isAuxSlot)) + ObjectSlotAttributes attr = attributes ? attributes[absSlotIndex] : ObjectSlotAttr_Default; + + if (attr & ObjectSlotAttr_Deleted) + { + return entry->slotIndex == Constants::NoSlot && !entry->mustBeWritable; + } + + if (attr & ObjectSlotAttr_Accessor) { return false; } -#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES - int maxInitializedLength = this->typePath->GetMaxInitializedLength(); - if (entry->mustBeWritable && FixPropsOnPathTypes() && (absSlotIndex >= maxInitializedLength || this->typePath->GetIsFixedFieldAt(absSlotIndex, this->GetPathLength()))) + Js::PropertyIndex relSlotIndex = AdjustValidSlotIndexForInlineSlots(absSlotIndex); + if (relSlotIndex != entry->slotIndex || ((absSlotIndex >= GetInlineSlotCapacity()) != entry->isAuxSlot)) { return false; } + + if (entry->mustBeWritable) + { + if (!(attr & ObjectSlotAttr_Writable)) + { + return false; + } + +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + int maxInitializedLength = this->GetTypePath()->GetMaxInitializedLength(); + if (FixPropsOnPathTypes() && (absSlotIndex >= maxInitializedLength || this->GetTypePath()->GetIsFixedFieldAt(absSlotIndex, this->GetPathLength()))) + { + return false; + } #endif + } } else { @@ -215,13 +418,13 @@ namespace Js BOOL PathTypeHandlerBase::GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) { - PropertyIndex index = typePath->LookupInline(propertyId, GetPathLength()); + PropertyIndex index = GetTypePath()->LookupInline(propertyId, GetPathLength()); if (index != Constants::NoSlot) { *value = instance->GetSlot(index); PropertyValueInfo::Set(info, instance, index); #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES - if (FixPropsOnPathTypes() && (index >= this->typePath->GetMaxInitializedLength() || this->typePath->GetIsFixedFieldAt(index, GetPathLength()))) + if (FixPropsOnPathTypes() && (index >= this->GetTypePath()->GetMaxInitializedLength() || this->GetTypePath()->GetIsFixedFieldAt(index, GetPathLength()))) { PropertyValueInfo::DisableStoreFieldCache(info); } @@ -266,7 +469,7 @@ namespace Js BOOL PathTypeHandlerBase::SetProperty(DynamicObject* instance, PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) { - return SetPropertyInternal(instance, propertyId, value, info, flags, SideEffects_Any); + return SetPropertyInternal(instance, propertyId, value, ObjectSlotAttr_Default, info, flags, SideEffects_Any); } BOOL PathTypeHandlerBase::SetProperty(DynamicObject* instance, JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) @@ -311,7 +514,8 @@ namespace Js SetPropertyUpdateSideEffect(instance, propertyId, value, possibleSideEffects); } - BOOL PathTypeHandlerBase::SetPropertyInternal(DynamicObject* instance, PropertyId propertyId, Var value, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) + template + BOOL PathTypeHandlerBase::SetPropertyInternal(DynamicObject* instance, PropertyId propertyId, Var value, ObjectSlotAttributes attr, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) { // Path type handler doesn't support pre-initialization (PropertyOperation_PreInit). Pre-initialized properties // will get marked as fixed when pre-initialized and then as non-fixed when their actual values are set. @@ -331,7 +535,12 @@ namespace Js Assert(instance->GetDynamicType()->GetIsShared() == GetIsShared()); - this->SetSlotAndCache(instance, propertyId, nullptr, index, value, info, flags, possibleSideEffects); + if (setAttributes) + { + this->SetAttributesHelper(instance, propertyId, index, GetAttributeArray(), attr); + } + PathTypeHandlerBase *newTypeHandler = PathTypeHandlerBase::FromTypeHandler(instance->GetDynamicType()->GetTypeHandler()); + newTypeHandler->SetSlotAndCache(instance, propertyId, nullptr, index, value, info, flags, possibleSideEffects); return true; } @@ -340,10 +549,17 @@ namespace Js uint32 indexVal; if (scriptContext->IsNumericPropertyId(propertyId, &indexVal)) { + if (setAttributes) + { + if (attr != ObjectSlotAttr_Default) + { + return this->ConvertToTypeWithItemAttributes(instance)->SetItemWithAttributes(instance, indexVal, value, ObjectSlotAttributesToPropertyAttributes(attr)); + } + } return PathTypeHandlerBase::SetItem(instance, indexVal, value, PropertyOperation_None); } - return PathTypeHandlerBase::AddPropertyInternal(instance, propertyId, value, info, flags, possibleSideEffects); + return PathTypeHandlerBase::AddPropertyInternal(instance, propertyId, value, attr, info, flags, possibleSideEffects); } void PathTypeHandlerBase::MoveAuxSlotsToObjectHeader(DynamicObject *const object) @@ -441,7 +657,7 @@ namespace Js predecessorType->ShareType(); #if ENABLE_FIXED_FIELDS - this->typePath->ClearSingletonInstanceIfSame(object); + this->GetTypePath()->ClearSingletonInstanceIfSame(object); #endif object->ReplaceTypeWithPredecessorType(predecessorType); @@ -467,6 +683,17 @@ namespace Js return TRUE; } + ObjectSlotAttributes attr = this->GetAttributeArray(index); + if (attr & ObjectSlotAttr_Deleted) + { + return TRUE; + } + if (!(attr & ObjectSlotAttr_Configurable)) + { + JavascriptError::ThrowCantDelete(PropertyOperation_None, scriptContext, scriptContext->GetPropertyName(propertyId)->GetBuffer()); + return FALSE; + } + uint16 pathLength = GetPathLength(); if ((index + 1) == pathLength && @@ -485,7 +712,7 @@ namespace Js return deleteResult; } - + BOOL PathTypeHandlerBase::IsEnumerable(DynamicObject* instance, PropertyId propertyId) { return true; @@ -501,28 +728,85 @@ namespace Js return true; } - BOOL PathTypeHandlerBase::SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) + BOOL PathTypeHandlerBase::SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) { -#ifdef PROFILE_TYPES - instance->GetScriptContext()->convertPathToDictionaryCount3++; -#endif - return value || ConvertToSimpleDictionaryType(instance, GetPathLength())->SetEnumerable(instance, propertyId, value); + if (value) + { + return true; + } + + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetConfigurable(instance, propertyId, value); + } + } + return true; + } + + return SetAttributesHelper(instance, propertyId, propertyIndex, nullptr, (ObjectSlotAttributes)(ObjectSlotAttr_Default & ~ObjectSlotAttr_Configurable)); } - BOOL PathTypeHandlerBase::SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) + BOOL PathTypeHandlerBase::SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) { -#ifdef PROFILE_TYPES - instance->GetScriptContext()->convertPathToDictionaryCount3++; -#endif - return value || ConvertToSimpleDictionaryType(instance, GetPathLength())->SetWritable(instance, propertyId, value); + if (value) + { + return true; + } + + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetEnumerable(instance, propertyId, value); + } + } + return true; + } + + return SetAttributesHelper(instance, propertyId, propertyIndex, nullptr, (ObjectSlotAttributes)(ObjectSlotAttr_Default & ~ObjectSlotAttr_Enumerable)); } - BOOL PathTypeHandlerBase::SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) + BOOL PathTypeHandlerBase::SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) { -#ifdef PROFILE_TYPES - instance->GetScriptContext()->convertPathToDictionaryCount3++; -#endif - return value || ConvertToSimpleDictionaryType(instance, GetPathLength())->SetConfigurable(instance, propertyId, value); + if (value) + { + return true; + } + + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetWritable(instance, propertyId, value); + } + } + return true; + } + + return SetAttributesHelper(instance, propertyId, propertyIndex, nullptr, (ObjectSlotAttributes)(ObjectSlotAttr_Default & ~ObjectSlotAttr_Writable)); } BOOL PathTypeHandlerBase::SetAccessors(DynamicObject* instance, PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags) @@ -619,9 +903,10 @@ namespace Js RecyclerWeakReference* newTypeWeakRef = nullptr; DynamicType * oldType = instance->GetDynamicType(); + PathTypeSuccessorKey key(operationInternalPropertyRecord->GetPropertyId(), ObjectSlotAttr_Default); // See if we already have shared type for this type and convert to it, otherwise create a new one. - if (!GetSuccessor(operationInternalPropertyRecord, &newTypeWeakRef) || newTypeWeakRef->Get() == nullptr) + if (!GetSuccessor(key, &newTypeWeakRef) || newTypeWeakRef->Get() == nullptr) { // Convert to new shared type with shared simple dictionary type handler and call operation on it. SimpleDictionaryTypeHandlerWithNontExtensibleSupport* newTypeHandler = ConvertToSimpleDictionaryType @@ -634,7 +919,7 @@ namespace Js ScriptContext * scriptContext = instance->GetScriptContext(); Recycler * recycler = scriptContext->GetRecycler(); - SetSuccessor(oldType, operationInternalPropertyRecord, recycler->CreateWeakReferenceHandle(newType), scriptContext); + SetSuccessor(oldType, key, recycler->CreateWeakReferenceHandle(newType), scriptContext); return operation(newTypeHandler); } else @@ -664,12 +949,12 @@ namespace Js return TRUE; } - DynamicType* PathTypeHandlerBase::PromoteType(DynamicObject* instance, const PropertyRecord* propertyRecord, PropertyIndex* propertyIndex) + DynamicType* PathTypeHandlerBase::PromoteType(DynamicObject* instance, const PathTypeSuccessorKey key, PropertyIndex* propertyIndex) { ScriptContext* scriptContext = instance->GetScriptContext(); DynamicType* currentType = instance->GetDynamicType(); - DynamicType* nextType = this->PromoteType(currentType, propertyRecord, false, scriptContext, instance, propertyIndex); + DynamicType* nextType = this->PromoteType(currentType, key, false, scriptContext, instance, propertyIndex); PathTypeHandlerBase* nextPath = (PathTypeHandlerBase*) nextType->GetTypeHandler(); instance->EnsureSlots(this->GetSlotCapacity(), nextPath->GetSlotCapacity(), scriptContext, nextType->GetTypeHandler()); @@ -737,7 +1022,7 @@ namespace Js { if (DynamicTypeHandler::AreSingletonInstancesNeeded()) { - RecyclerWeakReference* curSingletonInstance = oldTypeHandler->typePath->GetSingletonInstance(); + RecyclerWeakReference* curSingletonInstance = oldTypeHandler->GetTypePath()->GetSingletonInstance(); if (curSingletonInstance != nullptr && curSingletonInstance->Get() == instance) { newTypeHandler->SetSingletonInstance(curSingletonInstance); @@ -756,6 +1041,7 @@ namespace Js bool transferUsedAsFixed = ((oldTypeHandler->GetFlags() & IsPrototypeFlag) != 0 || (oldTypeHandler->GetIsOrMayBecomeShared() && !DynamicTypeHandler::IsolatePrototypes())); #endif + ObjectSlotAttributes * attributes = this->GetAttributeArray(); for (PropertyIndex i = 0; i < oldTypeHandler->GetPathLength(); i++) { #if ENABLE_FIXED_FIELDS @@ -765,8 +1051,8 @@ namespace Js // the type handler says the property is initialized, the current instance may not have a value for it. Check for value != null. if (PathTypeHandlerBase::FixPropsOnPathTypes()) { - TypePath * typePath = oldTypeHandler->typePath; - newTypeHandler->Add(typePath->GetPropertyId(i), PropertyDynamicTypeDefaults, + TypePath * typePath = oldTypeHandler->GetTypePath(); + newTypeHandler->Add(typePath->GetPropertyId(i), attributes ? ObjectSlotAttributesToPropertyAttributes(attributes[i]) : PropertyDynamicTypeDefaults, i < typePath->GetMaxInitializedLength(), transferFixed && typePath->GetIsFixedFieldAt(i, oldTypeHandler->GetPathLength()), transferUsedAsFixed && typePath->GetIsUsedFixedFieldAt(i, oldTypeHandler->GetPathLength()), @@ -775,7 +1061,7 @@ namespace Js else #endif { - newTypeHandler->Add(oldTypeHandler->typePath->GetPropertyId(i), PropertyDynamicTypeDefaults, true, false, false, scriptContext); + newTypeHandler->Add(oldTypeHandler->GetTypePath()->GetPropertyId(i), attributes ? ObjectSlotAttributesToPropertyAttributes(attributes[i]) : PropertyDynamicTypeDefaults, true, false, false, scriptContext); } } @@ -783,7 +1069,7 @@ namespace Js if (PathTypeHandlerBase::FixPropsOnPathTypes()) { Assert(oldTypeHandler->HasSingletonInstanceOnlyIfNeeded()); - oldTypeHandler->typePath->ClearSingletonInstanceIfSame(instance); + oldTypeHandler->GetTypePath()->ClearSingletonInstanceIfSame(instance); } #endif @@ -937,7 +1223,7 @@ namespace Js Assert(oldTypeHandler->HasSingletonInstanceOnlyIfNeeded()); if (DynamicTypeHandler::AreSingletonInstancesNeeded()) { - RecyclerWeakReference* curSingletonInstance = oldTypeHandler->typePath->GetSingletonInstance(); + RecyclerWeakReference* curSingletonInstance = oldTypeHandler->GetTypePath()->GetSingletonInstance(); if (curSingletonInstance != nullptr && curSingletonInstance->Get() == instance) { newTypeHandler->SetSingletonInstance(curSingletonInstance); @@ -968,13 +1254,15 @@ namespace Js // and mark every field as fixed, because we will always take a type transition. We have to remember to respect the switches as // to which kinds of properties we should fix, and for that we need the values from the instance. Even if the type handler // says the property is initialized, the current instance may not have a value for it. Check for value != null. + + ObjectSlotAttributes * attributes = this->GetAttributeArray(); for (PropertyIndex i = 0; i < oldTypeHandler->GetPathLength(); i++) { #if ENABLE_FIXED_FIELDS if (PathTypeHandlerBase::FixPropsOnPathTypes()) { - Js::TypePath * typePath = oldTypeHandler->typePath; - newTypeHandler->Add(typePath->GetPropertyId(i), PropertyDynamicTypeDefaults, + Js::TypePath * typePath = oldTypeHandler->GetTypePath(); + newTypeHandler->Add(typePath->GetPropertyId(i), attributes ? ObjectSlotAttributesToPropertyAttributes(attributes[i]) : PropertyDynamicTypeDefaults, i < typePath->GetMaxInitializedLength(), transferIsFixed && typePath->GetIsFixedFieldAt(i, GetPathLength()), transferUsedAsFixed && typePath->GetIsUsedFixedFieldAt(i, GetPathLength()), @@ -983,7 +1271,7 @@ namespace Js else #endif { - newTypeHandler->Add(oldTypeHandler->typePath->GetPropertyId(i), PropertyDynamicTypeDefaults, true, false, false, scriptContext); + newTypeHandler->Add(oldTypeHandler->GetTypePath()->GetPropertyId(i), attributes ? ObjectSlotAttributesToPropertyAttributes(attributes[i]) : PropertyDynamicTypeDefaults, true, false, false, scriptContext); } // No need to clear fixed fields not used as fixed, because we never convert during pre-creation of type handlers and we always @@ -996,7 +1284,7 @@ namespace Js if (PathTypeHandlerBase::FixPropsOnPathTypes()) { Assert(oldTypeHandler->HasSingletonInstanceOnlyIfNeeded()); - oldTypeHandler->typePath->ClearSingletonInstanceIfSame(instance); + oldTypeHandler->GetTypePath()->ClearSingletonInstanceIfSame(instance); } #endif @@ -1033,9 +1321,9 @@ namespace Js BOOL PathTypeHandlerBase::SetPropertyWithAttributes(DynamicObject* instance, PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) { - if (attributes == PropertyDynamicTypeDefaults) + if (ObjectSlotAttributesContains(attributes)) { - return PathTypeHandlerBase::SetPropertyInternal(instance, propertyId, value, info, flags, possibleSideEffects); + return PathTypeHandlerBase::SetPropertyInternal(instance, propertyId, value, PropertyAttributesToObjectSlotAttributes(attributes), info, flags, possibleSideEffects); } else { @@ -1045,7 +1333,7 @@ namespace Js BOOL PathTypeHandlerBase::SetAttributes(DynamicObject* instance, PropertyId propertyId, PropertyAttributes attributes) { - if ( (attributes & PropertyDynamicTypeDefaults) != PropertyDynamicTypeDefaults) + if (!ObjectSlotAttributesContains(attributes)) { #ifdef PROFILE_TYPES instance->GetScriptContext()->convertPathToDictionaryCount3++; @@ -1054,7 +1342,21 @@ namespace Js return ConvertToSimpleDictionaryType(instance, GetPathLength())->SetAttributes(instance, propertyId, attributes); } - return true; + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (instance->HasObjectArray() && attributes != PropertyDynamicTypeDefaults) + { + const PropertyRecord * propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + this->ConvertToTypeWithItemAttributes(instance)->SetItemAttributes(instance, propertyRecord->GetNumericValue(), attributes); + } + } + return true; + } + + return SetAttributesHelper(instance, propertyId, propertyIndex, GetAttributeArray(), PropertyAttributesToObjectSlotAttributes(attributes)); } BOOL PathTypeHandlerBase::GetAttributesWithPropertyIndex(DynamicObject * instance, PropertyId propertyId, BigPropertyIndex index, PropertyAttributes * attributes) @@ -1110,13 +1412,13 @@ namespace Js #ifdef PROFILE_OBJECT_LITERALS { RecyclerWeakReference* nextTypeWeakRef; - if (!pathHandler->GetSuccessor(scriptContext->GetPropertyName(propertyId), &nextTypeWeakRef) || nextTypeWeakRef->Get() == nullptr) + if (!pathHandler->GetSuccessor(PathTypeSuccessorKey(propertyId, ObjectSlotAttr_Default), &nextTypeWeakRef) || nextTypeWeakRef->Get() == nullptr) { scriptContext->objectLiteralPathCount++; } } #endif - type = pathHandler->PromoteType(type, scriptContext->GetPropertyName(propertyId), shareType, scriptContext, nullptr, &propertyIndex); + type = pathHandler->PromoteType(type, PathTypeSuccessorKey(propertyId, ObjectSlotAttr_Default), shareType, scriptContext, nullptr, &propertyIndex); } } else if (count <= static_cast(SimpleDictionaryTypeHandler::MaxPropertyIndexSize)) @@ -1169,7 +1471,7 @@ namespace Js } template - DynamicType* PathTypeHandlerBase::PromoteType(DynamicType* predecessorType, const PropertyRecord* propertyRecord, bool shareType, ScriptContext* scriptContext, DynamicObject* instance, PropertyIndex* propertyIndex) + DynamicType* PathTypeHandlerBase::PromoteType(DynamicType* predecessorType, const PathTypeSuccessorKey key, bool shareType, ScriptContext* scriptContext, DynamicObject* instance, PropertyIndex* propertyIndex) { Assert(propertyIndex != nullptr); Assert(isObjectLiteral || instance != nullptr); @@ -1178,27 +1480,36 @@ namespace Js PropertyIndex index; DynamicType * nextType; RecyclerWeakReference* nextTypeWeakRef = nullptr; + const PropertyRecord *propertyRecord = scriptContext->GetPropertyName(key.GetPropertyId()); PathTypeHandlerBase * nextPath; - if (!GetSuccessor(propertyRecord, &nextTypeWeakRef) || nextTypeWeakRef->Get() == nullptr) + if (!GetSuccessor(key, &nextTypeWeakRef) || nextTypeWeakRef->Get() == nullptr) { + TypePath * newTypePath = GetTypePath(); + uint8 oldPathSize = GetTypePath()->GetPathSize(); + + ObjectSlotAttributes *oldAttributes = GetAttributeArray(); + ObjectSlotAttributes *newAttributes = oldAttributes; + SetterSlotIndex *oldSetters = GetSetterSlots(); + SetterSlotIndex *newSetters = oldSetters; + bool addingAccessor = (key.GetAttributes() & ObjectSlotAttr_Accessor) && (!oldAttributes || !(oldAttributes[GetPathLength()] & ObjectSlotAttr_Accessor)); + + bool branching = GetTypePath()->GetPathLength() > GetPathLength(); + bool growing = !branching && GetTypePath()->GetPathLength() + addingAccessor >= GetTypePath()->GetPathSize(); #if ENABLE_FIXED_FIELDS #ifdef ENABLE_DEBUG_CONFIG_OPTIONS DynamicType* oldType = predecessorType; RecyclerWeakReference* oldSingletonInstance = GetSingletonInstance(); - bool branching = typePath->GetPathLength() > GetPathLength(); TraceFixedFieldsBeforeTypeHandlerChange(branching ? _u("branching") : _u("advancing"), _u("PathTypeHandler"), _u("PathTypeHandler"), instance, this, oldType, oldSingletonInstance); #endif #endif - TypePath * newTypePath = typePath; - - if (typePath->GetPathLength() > GetPathLength()) + if (branching) { // We need to branch the type path. - newTypePath = typePath->Branch(recycler, GetPathLength(), GetIsOrMayBecomeShared() && !IsolatePrototypes()); + newTypePath = GetTypePath()->Branch(recycler, GetPathLength(), GetIsOrMayBecomeShared() && !IsolatePrototypes()); #ifdef PROFILE_TYPES scriptContext->branchCount++; @@ -1209,22 +1520,50 @@ namespace Js scriptContext->objectLiteralBranchCount++; } #endif + + if (key.GetAttributes() != ObjectSlotAttr_Default || oldAttributes != nullptr) + { + newAttributes = this->UpdateAttributes(recycler, oldAttributes, oldPathSize, newTypePath); + } + + if ((key.GetAttributes() & ObjectSlotAttr_Accessor) || oldSetters != nullptr) + { + newSetters = this->UpdateSetterSlots(recycler, oldSetters, oldPathSize, newTypePath); + } } - else if (typePath->GetPathLength() == typePath->GetPathSize()) + else if (growing) { // We need to grow the type path. - newTypePath = typePath->Grow(recycler); + newTypePath = GetTypePath()->Grow(recycler); + + if (key.GetAttributes() != ObjectSlotAttr_Default || oldAttributes != nullptr) + { + newAttributes = this->UpdateAttributes(recycler, oldAttributes, oldPathSize, newTypePath); + } + + if ((key.GetAttributes() & ObjectSlotAttr_Accessor) || oldSetters != nullptr) + { + newSetters = this->UpdateSetterSlots(recycler, oldSetters, oldPathSize, newTypePath); + } // Update all the predecessor types that use this TypePath to the new TypePath. // This will allow the old TypePath to be collected, and will ensure that the // fixed field info is correct for those types. PathTypeHandlerBase * typeHandlerToUpdate = this; - TypePath * oldTypePath = typePath; + TypePath * oldTypePath = GetTypePath(); while (true) { - typeHandlerToUpdate->typePath = newTypePath; + typeHandlerToUpdate->SetTypePath(newTypePath); + if (oldAttributes && typeHandlerToUpdate->GetAttributeArray() == oldAttributes) + { + typeHandlerToUpdate->SetAttributeArray(newAttributes); + } + if (oldSetters && typeHandlerToUpdate->GetSetterSlots() == oldSetters) + { + typeHandlerToUpdate->SetSetterSlots(newSetters); + } DynamicType * currPredecessorType = typeHandlerToUpdate->GetPredecessorType(); if (currPredecessorType == nullptr) @@ -1233,13 +1572,25 @@ namespace Js } Assert(currPredecessorType->GetTypeHandler()->IsPathTypeHandler()); - typeHandlerToUpdate = (PathTypeHandlerBase *)currPredecessorType->GetTypeHandler(); - if (typeHandlerToUpdate->typePath != oldTypePath) + typeHandlerToUpdate = PathTypeHandlerBase::FromTypeHandler(currPredecessorType->GetTypeHandler()); + if (typeHandlerToUpdate->GetTypePath() != oldTypePath) { break; } } } + else + { + if (key.GetAttributes() != ObjectSlotAttr_Default && oldAttributes == nullptr) + { + newAttributes = this->UpdateAttributes(recycler, nullptr, oldPathSize, newTypePath); + } + + if ((key.GetAttributes() & ObjectSlotAttr_Accessor) && oldSetters == nullptr) + { + newSetters = this->UpdateSetterSlots(recycler, nullptr, oldPathSize, newTypePath); + } + } index = (PropertyIndex)newTypePath->AddInternal(propertyRecord); @@ -1257,7 +1608,16 @@ namespace Js #else bool markTypeAsShared = true; #endif - nextPath = SimplePathTypeHandler::New(scriptContext, newTypePath, newPropertyCount, newSlotCapacity, newInlineSlotCapacity, newOffsetOfInlineSlots, true, markTypeAsShared, predecessorType); + + if (key.GetAttributes() == ObjectSlotAttr_Default && oldAttributes == nullptr) + { + nextPath = SimplePathTypeHandlerNoAttr::New(scriptContext, newTypePath, newPropertyCount, newSlotCapacity, newInlineSlotCapacity, newOffsetOfInlineSlots, true, markTypeAsShared, predecessorType); + } + else + { + newAttributes[index] = key.GetAttributes(); + nextPath = SimplePathTypeHandlerWithAttr::New(scriptContext, newTypePath, newAttributes, newSetters, newPropertyCount, newSlotCapacity, newInlineSlotCapacity, newOffsetOfInlineSlots, true, markTypeAsShared, predecessorType); + } if (!markTypeAsShared) nextPath->SetMayBecomeShared(); Assert(nextPath->GetHasOnlyWritableDataProperties()); nextPath->CopyPropertyTypes(PropertyTypesWritableDataOnly | PropertyTypesWritableDataOnlyDetection, GetPropertyTypes()); @@ -1290,7 +1650,7 @@ namespace Js markTypeAsShared ? nextType->SetIsLockedAndShared() : nextType->SetIsLocked(); } - SetSuccessor(predecessorType, propertyRecord, recycler->CreateWeakReferenceHandle(nextType), scriptContext); + SetSuccessor(predecessorType, key, recycler->CreateWeakReferenceHandle(nextType), scriptContext); // We just extended the current type path to a new tip or created a brand new type path. We should // be at the tip of the path and there should be no instances there yet. Assert(nextPath->GetPathLength() == newTypePath->GetPathLength()); @@ -1330,7 +1690,7 @@ namespace Js if (!nextPath->GetIsShared()) { nextPath->AddBlankFieldAt(propertyRecord->GetPropertyId(), index, scriptContext); - nextPath->DoShareTypeHandlerInternal(scriptContext); + nextPath->DoShareTypeHandlerInternal(scriptContext); } nextType->ShareType(); } @@ -1339,15 +1699,68 @@ namespace Js Assert(!IsolatePrototypes() || !GetIsOrMayBecomeShared() || !GetIsPrototype()); nextPath->SetFlags(IsPrototypeFlag, this->GetFlags()); - Assert(this->GetHasOnlyWritableDataProperties() == nextPath->GetHasOnlyWritableDataProperties()); + Assert(this->GetHasOnlyWritableDataProperties() == nextPath->GetHasOnlyWritableDataProperties() || !(key.GetAttributes() & ObjectSlotAttr_Writable)); Assert(this->GetIsInlineSlotCapacityLocked() == nextPath->GetIsInlineSlotCapacityLocked()); nextPath->SetPropertyTypes(PropertyTypesWritableDataOnlyDetection, this->GetPropertyTypes()); + if (!(key.GetAttributes() & ObjectSlotAttr_Writable)) + { + nextPath->ClearHasOnlyWritableDataProperties(); + if (nextPath->GetFlags() & IsPrototypeFlag) + { + scriptContext->InvalidateStoreFieldCaches(key.GetPropertyId()); + instance->GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties(); + } + } (*propertyIndex) = index; return nextType; } + ObjectSlotAttributes * PathTypeHandlerBase::UpdateAttributes(Recycler * recycler, ObjectSlotAttributes * oldAttributes, uint8 oldPathSize, TypePath * newTypePath) + { + ObjectSlotAttributes * newAttributes = RecyclerNewArrayLeaf(recycler, ObjectSlotAttributes, newTypePath->GetPathSize()); + uint8 initStart; + if (oldAttributes == nullptr) + { + initStart = 0; + } + else + { + // In branching cases, the new type path may be shorter than the old. + initStart = min(newTypePath->GetPathSize(), oldPathSize); + memcpy(newAttributes, oldAttributes, sizeof(ObjectSlotAttributes) * initStart); + } + for (uint8 i = initStart; i < newTypePath->GetPathSize(); i++) + { + newAttributes[i] = ObjectSlotAttr_Default; + } + + return newAttributes; + } + + SetterSlotIndex * PathTypeHandlerBase::UpdateSetterSlots(Recycler * recycler, SetterSlotIndex * oldSetters, uint8 oldPathSize, TypePath * newTypePath) + { + SetterSlotIndex * newSetters = RecyclerNewArrayLeaf(recycler, SetterSlotIndex, newTypePath->GetPathSize()); + uint8 initStart; + if (oldSetters == nullptr) + { + initStart = 0; + } + else + { + // In branching cases, the new type path may be shorter than the old. + initStart = min(newTypePath->GetPathSize(), oldPathSize); + memcpy(newSetters, oldSetters, sizeof(SetterSlotIndex) * initStart); + } + for (uint8 i = initStart; i < newTypePath->GetPathSize(); i++) + { + newSetters[i] = NoSetterSlot; + } + + return newSetters; + } + void PathTypeHandlerBase::ResetTypeHandler(DynamicObject * instance) { @@ -1370,7 +1783,7 @@ namespace Js Js::ScriptContext* scriptContext = instance->GetScriptContext(); for (PropertyIndex propertyIndex = 0; propertyIndex < propertyCount; propertyIndex++) { - PropertyId propertyId = this->typePath->GetPropertyIdUnchecked(propertyIndex)->GetPropertyId(); + PropertyId propertyId = this->GetTypePath()->GetPropertyIdUnchecked(propertyIndex)->GetPropertyId(); InvalidateFixedFieldAt(propertyId, propertyIndex, scriptContext); } } @@ -1398,7 +1811,7 @@ namespace Js ScriptContext* scriptContext = instance->GetScriptContext(); for (PropertyIndex propertyIndex = 0; propertyIndex < propertyCount; propertyIndex++) { - PropertyId propertyId = this->typePath->GetPropertyIdUnchecked(propertyIndex)->GetPropertyId(); + PropertyId propertyId = this->GetTypePath()->GetPropertyIdUnchecked(propertyIndex)->GetPropertyId(); InvalidateFixedFieldAt(propertyId, propertyIndex, scriptContext); } } @@ -1412,16 +1825,17 @@ namespace Js BOOL PathTypeHandlerBase::AddProperty(DynamicObject * instance, PropertyId propertyId, Js::Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) { - if (attributes != PropertyDynamicTypeDefaults) + if (!ObjectSlotAttributesContains(attributes)) { + // Setting an attribute that PathTypeHandler can't express Assert(propertyId != Constants::NoProperty); PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); return ConvertToSimpleDictionaryType(instance, GetPathLength() + 1)->AddProperty(instance, propertyRecord, value, attributes, info, flags, possibleSideEffects); } - return AddPropertyInternal(instance, propertyId, value, info, flags, possibleSideEffects); + return AddPropertyInternal(instance, propertyId, value, PropertyAttributesToObjectSlotAttributes(attributes), info, flags, possibleSideEffects); } - BOOL PathTypeHandlerBase::AddPropertyInternal(DynamicObject * instance, PropertyId propertyId, Js::Var value, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) + BOOL PathTypeHandlerBase::AddPropertyInternal(DynamicObject * instance, PropertyId propertyId, Js::Var value, ObjectSlotAttributes attr, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects) { ScriptContext* scriptContext = instance->GetScriptContext(); @@ -1443,7 +1857,7 @@ namespace Js } PropertyIndex index; - DynamicType* newType = PromoteType(instance, propertyRecord, &index); + DynamicType* newType = PromoteType(instance, PathTypeSuccessorKey(propertyId, attr), &index); Assert(instance->GetTypeHandler()->IsPathTypeHandler()); PathTypeHandlerBase* newTypeHandler = (PathTypeHandlerBase*)newType->GetTypeHandler(); @@ -1521,27 +1935,49 @@ namespace Js Assert(IsObjectHeaderInlinedTypeHandler()); // Clone the type Path here to evolve separately - uint16 pathLength = typePath->GetPathLength(); + uint16 pathLength = GetTypePath()->GetPathLength(); TypePath * clonedPath = TypePath::New(library->GetRecycler(), pathLength); for (PropertyIndex i = 0; i < pathLength; i++) { - clonedPath->assignments[i] = typePath->assignments[i]; + clonedPath->assignments[i] = GetTypePath()->assignments[i]; clonedPath->AddInternal(clonedPath->assignments[i]); } // We don't copy the fixed fields, as we will be sharing this type anyways later and the fixed fields vector has to be invalidated. - SimplePathTypeHandler *const clonedTypeHandler = - SimplePathTypeHandler::New( - library->GetScriptContext(), - clonedPath, - GetPathLength(), - static_cast(GetSlotCapacity()), - GetInlineSlotCapacity() - GetObjectHeaderInlinableSlotCapacity(), - sizeof(DynamicObject), - false, - false); + SimplePathTypeHandler * clonedTypeHandler; + ObjectSlotAttributes *attributes = this->GetAttributeArray(); + if (attributes == nullptr) + { + clonedTypeHandler = + SimplePathTypeHandlerNoAttr::New( + library->GetScriptContext(), + clonedPath, + GetPathLength(), + static_cast(GetSlotCapacity()), + GetInlineSlotCapacity() - GetObjectHeaderInlinableSlotCapacity(), + sizeof(DynamicObject), + false, + false); + } + else + { + clonedTypeHandler = + SimplePathTypeHandlerWithAttr::New( + library->GetScriptContext(), + clonedPath, + attributes, + GetSetterSlots(), + GetPathLength(), + static_cast(GetSlotCapacity()), + GetInlineSlotCapacity() - GetObjectHeaderInlinableSlotCapacity(), + sizeof(DynamicObject), + false, + false); + } clonedTypeHandler->SetMayBecomeShared(); + clonedTypeHandler->CopyPropertyTypes(PropertyTypesWritableDataOnly | PropertyTypesWritableDataOnlyDetection, this->GetPropertyTypes()); + return clonedTypeHandler; } @@ -1615,7 +2051,7 @@ namespace Js if (cachedDynamicType == nullptr) { - SimplePathTypeHandler* newTypeHandler = SimplePathTypeHandler::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, static_cast(this->GetSlotCapacity()), this->GetInlineSlotCapacity(), this->GetOffsetOfInlineSlots(), true, true); + SimplePathTypeHandlerNoAttr* newTypeHandler = SimplePathTypeHandlerNoAttr::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, static_cast(this->GetSlotCapacity()), this->GetInlineSlotCapacity(), this->GetOffsetOfInlineSlots(), true, true); cachedDynamicType = instance->DuplicateType(); cachedDynamicType->SetPrototype(newPrototype); @@ -1629,13 +2065,14 @@ namespace Js } // Promote type based on existing properties to get new type which will be cached and shared + ObjectSlotAttributes * attr = this->GetAttributeArray(); for (PropertyIndex i = 0; i < GetPropertyCount(); i++) { PathTypeHandlerBase * pathTypeHandler = (PathTypeHandlerBase*)cachedDynamicType->GetTypeHandler(); Js::PropertyId propertyId = GetPropertyId(scriptContext, i); PropertyIndex propertyIndex = GetPropertyIndex(propertyId); - cachedDynamicType = pathTypeHandler->PromoteType(cachedDynamicType, scriptContext->GetPropertyName(propertyId), true, scriptContext, instance, &propertyIndex); + cachedDynamicType = pathTypeHandler->PromoteType(cachedDynamicType, PathTypeSuccessorKey(propertyId, attr ? attr[propertyIndex] : ObjectSlotAttr_Default), true, scriptContext, instance, &propertyIndex); } if (useCache) @@ -1808,7 +2245,7 @@ namespace Js #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES if (FixPropsOnPathTypes()) { - return index < this->typePath->GetMaxInitializedLength() && !this->typePath->GetIsFixedFieldAt(index, this->GetPathLength()); + return index < this->GetTypePath()->GetMaxInitializedLength() && !this->GetTypePath()->GetIsFixedFieldAt(index, this->GetPathLength()); } else #endif @@ -1835,7 +2272,7 @@ namespace Js PropertyIndex index = PathTypeHandlerBase::GetPropertyIndex(propertyId); Assert(index != Constants::NoSlot); - return this->typePath->GetIsFixedFieldAt(index, GetPathLength()); + return this->GetTypePath()->GetIsFixedFieldAt(index, GetPathLength()); } bool PathTypeHandlerBase::HasSingletonInstance() const @@ -1846,7 +2283,7 @@ namespace Js return false; } - return this->typePath->HasSingletonInstance() && GetPathLength() >= this->typePath->GetMaxInitializedLength(); + return this->GetTypePath()->HasSingletonInstance() && GetPathLength() >= this->GetTypePath()->GetMaxInitializedLength(); } void PathTypeHandlerBase::DoShareTypeHandler(ScriptContext* scriptContext) @@ -1867,13 +2304,13 @@ namespace Js { if (invalidateFixedFields) { - if (this->typePath->GetMaxInitializedLength() < GetPathLength()) + if (this->GetTypePath()->GetMaxInitializedLength() < GetPathLength()) { - this->typePath->SetMaxInitializedLength(GetPathLength()); + this->GetTypePath()->SetMaxInitializedLength(GetPathLength()); } for (PropertyIndex index = 0; index < this->GetPathLength(); index++) { - InvalidateFixedFieldAt(this->typePath->GetPropertyIdUnchecked(index)->GetPropertyId(), index, scriptContext); + InvalidateFixedFieldAt(this->GetTypePath()->GetPropertyIdUnchecked(index)->GetPropertyId(), index, scriptContext); } } @@ -1881,7 +2318,7 @@ namespace Js Assert(HasSingletonInstanceOnlyIfNeeded()); if (HasSingletonInstance()) { - this->typePath->ClearSingletonInstance(); + this->GetTypePath()->ClearSingletonInstance(); } } #endif @@ -1896,10 +2333,10 @@ namespace Js // We are adding a new value where some other instance already has an existing value. If this is a fixed // field we must clear the bit. If the value was hard coded in the JIT-ed code, we must invalidate the guards. - if (this->typePath->GetIsUsedFixedFieldAt(index, GetPathLength())) + if (this->GetTypePath()->GetIsUsedFixedFieldAt(index, GetPathLength())) { // We may be a second instance chasing the singleton and invalidating fixed fields along the way. - // Assert(newTypeHandler->typePath->GetSingletonInstance() == instance); + // Assert(newTypeHandler->GetTypePath()->GetSingletonInstance() == instance); // Invalidate any JIT-ed code that hard coded this method. No need to invalidate store field // inline caches (which might quietly overwrite this fixed fields, because they have never been populated. @@ -1910,7 +2347,7 @@ namespace Js // If we're overwriting an existing value of this property, we don't consider the new one fixed. // This also means that it's ok to populate the inline caches for this property from now on. - this->typePath->ClearIsFixedFieldAt(index, GetPathLength()); + this->GetTypePath()->ClearIsFixedFieldAt(index, GetPathLength()); } void PathTypeHandlerBase::AddBlankFieldAt(Js::PropertyId propertyId, Js::PropertyIndex index, ScriptContext* scriptContext) @@ -1920,14 +2357,14 @@ namespace Js return; } - if (index >= this->typePath->GetMaxInitializedLength()) + if (index >= this->GetTypePath()->GetMaxInitializedLength()) { // We are adding a property where no instance property has been set before. We rely on properties being // added in order of indexes to be sure that we don't leave any uninitialized properties interspersed with // initialized ones, which could lead to incorrect behavior. See comment in TypePath::Branch. - AssertMsg(index == this->typePath->GetMaxInitializedLength(), "Adding properties out of order?"); + AssertMsg(index == this->GetTypePath()->GetMaxInitializedLength(), "Adding properties out of order?"); - this->typePath->AddBlankFieldAt(index, GetPathLength()); + this->GetTypePath()->AddBlankFieldAt(index, GetPathLength()); } else { @@ -1936,7 +2373,7 @@ namespace Js // We have now reached the most advanced instance along this path. If this instance is not the singleton instance, // then the former singleton instance (if any) is no longer a singleton. This instance could be the singleton // instance, if we just happen to set (overwrite) its last property. - if (index + 1 == this->typePath->GetMaxInitializedLength()) + if (index + 1 == this->GetTypePath()->GetMaxInitializedLength()) { // If we cleared the singleton instance while some fields remained fixed, the instance would // be collectible, and yet some code would expect to see values and call methods on it. We rely on the @@ -1945,7 +2382,7 @@ namespace Js // Otherwise, some fields could remain fixed (or even uninitialized) and we would have to spin off a loop here // to invalidate any remaining fixed fields Assert(HasSingletonInstanceOnlyIfNeeded()); - this->typePath->ClearSingletonInstance(); + this->GetTypePath()->ClearSingletonInstance(); } } @@ -1967,12 +2404,12 @@ namespace Js PathTypeHandlerBase* newTypeHandler = (PathTypeHandlerBase*)instance->GetTypeHandler(); - if (slotIndex >= newTypeHandler->typePath->GetMaxInitializedLength()) + if (slotIndex >= newTypeHandler->GetTypePath()->GetMaxInitializedLength()) { // We are adding a property where no instance property has been set before. We rely on properties being // added in order of indexes to be sure that we don't leave any uninitialized properties interspersed with // initialized ones, which could lead to incorrect behavior. See comment in TypePath::Branch. - AssertMsg(slotIndex == newTypeHandler->typePath->GetMaxInitializedLength(), "Adding properties out of order?"); + AssertMsg(slotIndex == newTypeHandler->GetTypePath()->GetMaxInitializedLength(), "Adding properties out of order?"); // Consider: It would be nice to assert the slot is actually null. However, we sometimes pre-initialize to // undefined or even some other special illegal value (for let or const, currently == null) @@ -1986,11 +2423,11 @@ namespace Js // Mark the newly added field as fixed and prevent population of inline caches. - newTypeHandler->typePath->AddSingletonInstanceFieldAt(instance, slotIndex, markAsFixed, newTypeHandler->GetPathLength()); + newTypeHandler->GetTypePath()->AddSingletonInstanceFieldAt(instance, slotIndex, markAsFixed, newTypeHandler->GetPathLength()); } else { - newTypeHandler->typePath->AddSingletonInstanceFieldAt(slotIndex, newTypeHandler->GetPathLength()); + newTypeHandler->GetTypePath()->AddSingletonInstanceFieldAt(slotIndex, newTypeHandler->GetPathLength()); } populateInlineCache = false; @@ -2002,7 +2439,7 @@ namespace Js // We have now reached the most advanced instance along this path. If this instance is not the singleton instance, // then the former singleton instance (if any) is no longer a singleton. This instance could be the singleton // instance, if we just happen to set (overwrite) its last property. - if (slotIndex + 1 == newTypeHandler->typePath->GetMaxInitializedLength()) + if (slotIndex + 1 == newTypeHandler->GetTypePath()->GetMaxInitializedLength()) { // If we cleared the singleton instance while some fields remained fixed, the instance would // be collectible, and yet some code would expect to see values and call methods on it. We rely on the @@ -2010,24 +2447,24 @@ namespace Js // on the type path. By the time we reach the singleton instance, all fixed fields will have been invalidated. // Otherwise, some fields could remain fixed (or even uninitialized) and we would have to spin off a loop here // to invalidate any remaining fixed fields - auto singletonWeakRef = newTypeHandler->typePath->GetSingletonInstance(); + auto singletonWeakRef = newTypeHandler->GetTypePath()->GetSingletonInstance(); if (singletonWeakRef != nullptr && instance != singletonWeakRef->Get()) { Assert(newTypeHandler->HasSingletonInstanceOnlyIfNeeded()); - newTypeHandler->typePath->ClearSingletonInstance(); + newTypeHandler->GetTypePath()->ClearSingletonInstance(); } } } // If we branched and this is the singleton instance, we need to remove it from this type handler. The only time // this can happen is when another not fully initialized instance is ahead of this one on the current path. - auto singletonWeakRef = this->typePath->GetSingletonInstance(); - if (newTypeHandler->typePath != this->typePath && singletonWeakRef != nullptr && singletonWeakRef->Get() == instance) + auto singletonWeakRef = this->GetTypePath()->GetSingletonInstance(); + if (newTypeHandler->GetTypePath() != this->GetTypePath() && singletonWeakRef != nullptr && singletonWeakRef->Get() == instance) { // If this is the singleton instance, there shouldn't be any other initialized instance ahead of it on the old path. - Assert(GetPathLength() >= this->typePath->GetMaxInitializedLength()); + Assert(GetPathLength() >= this->GetTypePath()->GetMaxInitializedLength()); Assert(HasSingletonInstanceOnlyIfNeeded()); - this->typePath->ClearSingletonInstance(); + this->GetTypePath()->ClearSingletonInstance(); } return populateInlineCache; @@ -2060,14 +2497,14 @@ namespace Js { #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES - if (this->typePath->GetMaxInitializedLength() < GetPathLength()) + if (this->GetTypePath()->GetMaxInitializedLength() < GetPathLength()) { return false; } for (PropertyIndex index = 0; index < this->GetPathLength(); index++) { - if (this->typePath->GetIsFixedFieldAt(index, this->GetPathLength())) + if (this->GetTypePath()->GetIsFixedFieldAt(index, this->GetPathLength())) { return false; } @@ -2087,7 +2524,7 @@ namespace Js int pathLength = GetPathLength(); for (PropertyIndex i = 0; i < pathLength; i++) { - if (this->typePath->GetIsFixedFieldAt(i, pathLength)) + if (this->GetTypePath()->GetIsFixedFieldAt(i, pathLength)) { return true; } @@ -2104,20 +2541,20 @@ namespace Js return false; } - PropertyIndex index = this->typePath->Lookup(propertyRecord->GetPropertyId(), GetPathLength()); + PropertyIndex index = this->GetTypePath()->Lookup(propertyRecord->GetPropertyId(), GetPathLength()); if (index == Constants::NoSlot) { AssertMsg(allowNonExistent, "Trying to get a fixed function instance for a non-existent property?"); return false; } - Var value = this->typePath->GetSingletonFixedFieldAt(index, GetPathLength(), requestContext); + Var value = this->GetTypePath()->GetSingletonFixedFieldAt(index, GetPathLength(), requestContext); if (value && ((IsFixedMethodProperty(propertyType) && JavascriptFunction::Is(value)) || IsFixedDataProperty(propertyType))) { *pProperty = value; if (markAsUsed) { - this->typePath->SetIsUsedFixedFieldAt(index, GetPathLength()); + this->GetTypePath()->SetIsUsedFixedFieldAt(index, GetPathLength()); } return true; } @@ -2133,17 +2570,17 @@ namespace Js { for (PropertyIndex i = 0; i < GetPathLength(); i++) { - Output::Print(_u(" %s %d%d%d,"), typePath->GetPropertyId(i)->GetBuffer(), - i < this->typePath->GetMaxInitializedLength() ? 1 : 0, - this->typePath->GetIsFixedFieldAt(i, GetPathLength()) ? 1 : 0, - this->typePath->GetIsUsedFixedFieldAt(i, GetPathLength()) ? 1 : 0); + Output::Print(_u(" %s %d%d%d,"), GetTypePath()->GetPropertyId(i)->GetBuffer(), + i < this->GetTypePath()->GetMaxInitializedLength() ? 1 : 0, + this->GetTypePath()->GetIsFixedFieldAt(i, GetPathLength()) ? 1 : 0, + this->GetTypePath()->GetIsUsedFixedFieldAt(i, GetPathLength()) ? 1 : 0); } } else { for (PropertyIndex i = 0; i < GetPathLength(); i++) { - Output::Print(_u(" %s %d%d%d,"), typePath->GetPropertyId(i)->GetBuffer(), 1, 0, 0); + Output::Print(_u(" %s %d%d%d,"), GetTypePath()->GetPropertyId(i)->GetBuffer(), 1, 0, 0); } } } @@ -2264,7 +2701,7 @@ namespace Js for(uint32 index = 0; index < plength; ++index) { - Js::PropertyId pid = typePath->GetPropertyIdUnchecked(index)->GetPropertyId(); + Js::PropertyId pid = GetTypePath()->GetPropertyIdUnchecked(index)->GetPropertyId(); if(DynamicTypeHandler::ShouldMarkPropertyId_TTD(pid)) { @@ -2277,10 +2714,12 @@ namespace Js uint32 PathTypeHandlerBase::ExtractSlotInfo_TTD(TTD::NSSnapType::SnapHandlerPropertyEntry* entryInfo, ThreadContext* threadContext, TTD::SlabAllocator& alloc) const { uint32 plength = this->GetPathLength(); - + ObjectSlotAttributes * attributes = this->GetAttributeArray(); + for(uint32 index = 0; index < plength; ++index) { - TTD::NSSnapType::ExtractSnapPropertyEntryInfo(entryInfo + index, typePath->GetPropertyIdUnchecked(index)->GetPropertyId(), PropertyDynamicTypeDefaults, TTD::NSSnapType::SnapEntryDataKindTag::Data); + PropertyAttributes attr = ObjectSlotAttributesToPropertyAttributes(attributes ? attributes[index] : ObjectSlotAttr_Default); + TTD::NSSnapType::ExtractSnapPropertyEntryInfo(entryInfo + index, GetTypePath()->GetPropertyIdUnchecked(index)->GetPropertyId(), attr, TTD::NSSnapType::SnapEntryDataKindTag::Data); } return plength; @@ -2289,7 +2728,7 @@ namespace Js Js::BigPropertyIndex PathTypeHandlerBase::GetPropertyIndex_EnumerateTTD(const Js::PropertyRecord* pRecord) { //The regular LookupInline is fine for path types - return (Js::BigPropertyIndex)this->typePath->LookupInline(pRecord->GetPropertyId(), GetPathLength()); + return (Js::BigPropertyIndex)this->GetTypePath()->LookupInline(pRecord->GetPropertyId(), GetPathLength()); } bool PathTypeHandlerBase::IsResetableForTTD(uint32 snapMaxIndex) const @@ -2298,40 +2737,20 @@ namespace Js } #endif - SimplePathTypeHandler * SimplePathTypeHandler::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + SimplePathTypeHandler::SimplePathTypeHandler(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : + PathTypeHandlerBase(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType), + successorKey(Constants::NoProperty, ObjectSlotAttr_None), + successorTypeWeakRef(nullptr) { - return New(scriptContext, typePath, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); } - SimplePathTypeHandler * SimplePathTypeHandler::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) - { - Assert(typePath != nullptr); -#ifdef PROFILE_TYPES - scriptContext->simplePathTypeHandlerCount++; -#endif - return RecyclerNew(scriptContext->GetRecycler(), SimplePathTypeHandler, typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); - } - - SimplePathTypeHandler * SimplePathTypeHandler::New(ScriptContext * scriptContext, SimplePathTypeHandler * typeHandler, bool isLocked, bool isShared) - { - Assert(typeHandler != nullptr); - return RecyclerNew(scriptContext->GetRecycler(), SimplePathTypeHandler, typeHandler->GetTypePath(), typeHandler->GetPathLength(), typeHandler->GetInlineSlotCapacity(), typeHandler->GetOffsetOfInlineSlots(), isLocked, isShared); - } - - SimplePathTypeHandler::SimplePathTypeHandler(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : - PathTypeHandlerBase(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType), - successorPropertyRecord(nullptr), - successorTypeWeakRef(nullptr) - { - } - - void SimplePathTypeHandler::ShrinkSlotAndInlineSlotCapacity(uint16 newInlineSlotCapacity) + void SimplePathTypeHandler::ShrinkSlotAndInlineSlotCapacity(uint16 newInlineSlotCapacity) { Assert(!this->GetIsInlineSlotCapacityLocked()); this->SetInlineSlotCapacity(newInlineSlotCapacity); this->SetSlotCapacity(newInlineSlotCapacity); this->SetIsInlineSlotCapacityLocked(); - if (this->successorPropertyRecord) + if (this->successorTypeWeakRef) { DynamicType * type = successorTypeWeakRef->Get(); if (type) @@ -2346,7 +2765,7 @@ namespace Js Assert(!GetIsInlineSlotCapacityLocked()); SetIsInlineSlotCapacityLocked(); - if (!successorPropertyRecord) + if (!successorTypeWeakRef) { return; } @@ -2372,7 +2791,7 @@ namespace Js { SetIsInlineSlotCapacityLocked(); - if (successorPropertyRecord) + if (successorTypeWeakRef) { DynamicType * type = successorTypeWeakRef->Get(); if (type) @@ -2398,7 +2817,7 @@ namespace Js Assert(GetIsInlineSlotCapacityLocked()); - if (!successorPropertyRecord) + if (!successorTypeWeakRef) { return; } @@ -2420,7 +2839,7 @@ namespace Js *maxPathLength = GetPathLength(); } - if (!successorPropertyRecord) + if (!successorTypeWeakRef) { return true; } @@ -2440,9 +2859,9 @@ namespace Js return true; } - bool SimplePathTypeHandler::GetSuccessor(const PropertyRecord* propertyRecord, RecyclerWeakReference ** typeWeakRef) + bool SimplePathTypeHandler::GetSuccessor(const PathTypeSuccessorKey successorKey, RecyclerWeakReference ** typeWeakRef) { - if (successorPropertyRecord != propertyRecord) + if (successorKey != this->successorKey) { *typeWeakRef = nullptr; return false; @@ -2451,11 +2870,11 @@ namespace Js return true; } - void SimplePathTypeHandler::SetSuccessor(DynamicType * type, const PropertyRecord* propertyRecord, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) + void SimplePathTypeHandler::SetSuccessorHelper(DynamicType * type, const PathTypeSuccessorKey key, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) { - if (!successorPropertyRecord || successorPropertyRecord == propertyRecord || !successorTypeWeakRef->Get()) + if (!successorTypeWeakRef || !successorTypeWeakRef->Get()) { - successorPropertyRecord = propertyRecord; + successorKey = key; successorTypeWeakRef = typeWeakRef; return; } @@ -2464,10 +2883,18 @@ namespace Js // (which can happen if we don't isolate prototypes but force type change on becoming proto), they will continue to do so. So // we will have two different type handlers at the exact same point in type path evolution sharing the same type path, and // consequently all fixed field info as well. This is fine, because fixed field management is done at the type path level. - PathTypeHandler * newTypeHandler = PathTypeHandler::New(scriptContext, GetTypePath(), GetPathLength(), static_cast(GetSlotCapacity()), GetInlineSlotCapacity(), GetOffsetOfInlineSlots(), true, true, GetPredecessorType()); - newTypeHandler->SetSuccessor(type, this->successorPropertyRecord, this->successorTypeWeakRef, scriptContext); - newTypeHandler->SetSuccessor(type, propertyRecord, typeWeakRef, scriptContext); - newTypeHandler->SetFlags(IsPrototypeFlag, GetFlags()); + PathTypeHandler * newTypeHandler; + if (attributes == nullptr) + { + newTypeHandler = PathTypeHandlerNoAttr::New(scriptContext, GetTypePath(), GetPathLength(), static_cast(GetSlotCapacity()), GetInlineSlotCapacity(), GetOffsetOfInlineSlots(), GetIsLocked(), GetIsShared(), GetPredecessorType()); + } + else + { + newTypeHandler = PathTypeHandlerWithAttr::New(scriptContext, GetTypePath(), attributes, accessors, GetPathLength(), static_cast(GetSlotCapacity()), GetInlineSlotCapacity(), GetOffsetOfInlineSlots(), GetIsLocked(), GetIsShared(), GetPredecessorType()); + } + newTypeHandler->SetSuccessor(type, this->successorKey, this->successorTypeWeakRef, scriptContext); + newTypeHandler->SetSuccessor(type, key, typeWeakRef, scriptContext); + newTypeHandler->SetFlags(IsPrototypeFlag | MayBecomeSharedFlag, GetFlags()); newTypeHandler->CopyPropertyTypes(PropertyTypesWritableDataOnly | PropertyTypesWritableDataOnlyDetection | PropertyTypesInlineSlotCapacityLocked, this->GetPropertyTypes()); // We don't transfer any fixed field data because we assume the type path remains the same. Assert(newTypeHandler->GetTypePath() == this->GetTypePath()); @@ -2480,6 +2907,25 @@ namespace Js #endif } + SimplePathTypeHandlerNoAttr * SimplePathTypeHandlerNoAttr::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + return New(scriptContext, typePath, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + SimplePathTypeHandlerNoAttr * SimplePathTypeHandlerNoAttr::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + Assert(typePath != nullptr); +#ifdef PROFILE_TYPES + scriptContext->simplePathTypeHandlerCount++; +#endif + return RecyclerNew(scriptContext->GetRecycler(), SimplePathTypeHandlerNoAttr, typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + SimplePathTypeHandlerNoAttr::SimplePathTypeHandlerNoAttr(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : + SimplePathTypeHandler(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType) + { + } + #if DBG_DUMP void SimplePathTypeHandler::Dump(unsigned indent) const { @@ -2487,24 +2933,234 @@ namespace Js } #endif - PathTypeHandler * PathTypeHandler::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + SimplePathTypeHandlerWithAttr * SimplePathTypeHandlerWithAttr::New(ScriptContext * scriptContext, TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) { - return New(scriptContext, typePath, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + return New(scriptContext, typePath, attributes, accessors, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); } - PathTypeHandler * PathTypeHandler::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + SimplePathTypeHandlerWithAttr * SimplePathTypeHandlerWithAttr::New(ScriptContext * scriptContext, TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) { Assert(typePath != nullptr); #ifdef PROFILE_TYPES - scriptContext->pathTypeHandlerCount++; + scriptContext->simplePathTypeHandlerCount++; #endif - return RecyclerNew(scriptContext->GetRecycler(), PathTypeHandler, typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + return RecyclerNew(scriptContext->GetRecycler(), SimplePathTypeHandlerWithAttr, typePath, attributes, accessors, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); } - PathTypeHandler * PathTypeHandler::New(ScriptContext * scriptContext, PathTypeHandler * typeHandler, bool isLocked, bool isShared) + SimplePathTypeHandlerWithAttr::SimplePathTypeHandlerWithAttr(TypePath *typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : + SimplePathTypeHandlerNoAttr(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType), + attributes(attributes), + accessors(accessors) { - Assert(typeHandler != nullptr); - return RecyclerNew(scriptContext->GetRecycler(), PathTypeHandler, typeHandler->GetTypePath(), typeHandler->GetPathLength(), static_cast(typeHandler->GetSlotCapacity()), typeHandler->GetInlineSlotCapacity(), typeHandler->GetOffsetOfInlineSlots(), isLocked, isShared); + } + + BOOL SimplePathTypeHandlerWithAttr::IsEnumerable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Enumerable); + } + return true; + } + + BOOL SimplePathTypeHandlerWithAttr::IsWritable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Writable); + } + return true; + } + + BOOL SimplePathTypeHandlerWithAttr::IsConfigurable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Configurable); + } + return true; + } + + BOOL SimplePathTypeHandlerWithAttr::SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetConfigurable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Configurable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Configurable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + + BOOL SimplePathTypeHandlerWithAttr::SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetEnumerable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Enumerable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Enumerable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + + BOOL SimplePathTypeHandlerWithAttr::SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetWritable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Writable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Writable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + +#if ENABLE_NATIVE_CODEGEN + bool SimplePathTypeHandlerWithAttr::IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) + { + return IsObjTypeSpecEquivalentHelper(type, attributes, record, failedPropertyIndex); + } + + bool SimplePathTypeHandlerWithAttr::IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry *entry) + { + return IsObjTypeSpecEquivalentHelper(type, attributes, entry); + } +#endif + + DescriptorFlags SimplePathTypeHandlerWithAttr::GetSetter(DynamicObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + return __super::GetSetter(instance, propertyId, setterValue, info, requestContext); + } + ObjectSlotAttributes attr = attributes[propertyIndex]; + if (attr & ObjectSlotAttr_Deleted) + { + return None; + } + + Assert(!(attr & ObjectSlotAttr_Accessor)); + + if (attr & ObjectSlotAttr_Writable) + { + return WritableData; + } + return Data; + } + + DescriptorFlags SimplePathTypeHandlerWithAttr::GetSetter(DynamicObject* instance, JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyRecord const* propertyRecord; + instance->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString, &propertyRecord); + return this->GetSetter(instance, propertyRecord->GetPropertyId(), setterValue, info, requestContext); + } + + BOOL SimplePathTypeHandlerWithAttr::GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyIndex index = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (index == Constants::NoSlot) + { + return __super::GetProperty(instance, originalInstance, propertyId, value, info, requestContext); + } + + *value = instance->GetSlot(index); + PropertyValueInfo::Set(info, instance, index, ObjectSlotAttributesToPropertyAttributes(attributes[index])); +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + if (FixPropsOnPathTypes() && (index >= this->GetTypePath()->GetMaxInitializedLength() || this->GetTypePath()->GetIsFixedFieldAt(index, GetPathLength()))) + { + PropertyValueInfo::DisableStoreFieldCache(info); + } +#endif + return true; + } + + BOOL SimplePathTypeHandlerWithAttr::GetProperty(DynamicObject* instance, Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) + { + // Consider: Implement actual string hash lookup + Assert(requestContext); + PropertyRecord const* propertyRecord; + char16 const * propertyName = propertyNameString->GetString(); + charcount_t const propertyNameLength = propertyNameString->GetLength(); + + if (instance->HasObjectArray()) + { + requestContext->GetOrAddPropertyRecord(propertyName, propertyNameLength, &propertyRecord); + } + else + { + requestContext->FindPropertyRecord(propertyName, propertyNameLength, &propertyRecord); + if (propertyRecord == nullptr) + { + *value = requestContext->GetMissingPropertyResult(); + return false; + } + } + return SimplePathTypeHandlerWithAttr::GetProperty(instance, originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext); + } + + BOOL SimplePathTypeHandlerWithAttr::GetAttributesWithPropertyIndex(DynamicObject * instance, PropertyId propertyId, BigPropertyIndex index, PropertyAttributes * attributes) + { + if (index < this->GetPathLength()) + { + Assert(this->GetPropertyId(instance->GetScriptContext(), index) == propertyId); + *attributes = ObjectSlotAttributesToPropertyAttributes(this->attributes[index]); + return true; + } + return false; } PathTypeHandler::PathTypeHandler(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : @@ -2522,7 +3178,7 @@ namespace Js this->SetIsInlineSlotCapacityLocked(); if (this->propertySuccessors) { - this->propertySuccessors->Map([newInlineSlotCapacity](PropertyId, RecyclerWeakReference * typeWeakReference) + this->propertySuccessors->Map([newInlineSlotCapacity](PathTypeSuccessorKey, RecyclerWeakReference * typeWeakReference) { DynamicType * type = typeWeakReference->Get(); if (type) @@ -2543,7 +3199,7 @@ namespace Js return; } - this->propertySuccessors->Map([](const PropertyId, RecyclerWeakReference* typeWeakReference) + this->propertySuccessors->Map([](const PathTypeSuccessorKey, RecyclerWeakReference* typeWeakReference) { DynamicType * type = typeWeakReference->Get(); if (!type) @@ -2571,7 +3227,7 @@ namespace Js if (propertySuccessors && propertySuccessors->Count() > 0) { - this->propertySuccessors->Map([](const PropertyId, RecyclerWeakReference * typeWeakReference) + this->propertySuccessors->Map([](const PathTypeSuccessorKey, RecyclerWeakReference * typeWeakReference) { DynamicType * type = typeWeakReference->Get(); if (!type) @@ -2606,7 +3262,7 @@ namespace Js return; } - this->propertySuccessors->Map([](const PropertyId, RecyclerWeakReference * typeWeakReference) + this->propertySuccessors->Map([](const PathTypeSuccessorKey, RecyclerWeakReference * typeWeakReference) { DynamicType * type = typeWeakReference->Get(); if (!type) @@ -2634,7 +3290,7 @@ namespace Js } bool result = true; - this->propertySuccessors->MapUntil([&result, maxPathLength](PropertyId, RecyclerWeakReference * typeWeakReference) -> bool + this->propertySuccessors->MapUntil([&result, maxPathLength](PathTypeSuccessorKey, RecyclerWeakReference * typeWeakReference) -> bool { DynamicType * type = typeWeakReference->Get(); if (!type) @@ -2658,9 +3314,9 @@ namespace Js return result; } - bool PathTypeHandler::GetSuccessor(const PropertyRecord* propertyRecord, RecyclerWeakReference ** typeWeakRef) + bool PathTypeHandler::GetSuccessor(const PathTypeSuccessorKey key, RecyclerWeakReference ** typeWeakRef) { - if (!propertySuccessors || !propertySuccessors->TryGetValue(propertyRecord->GetPropertyId(), typeWeakRef)) + if (!propertySuccessors || !propertySuccessors->TryGetValue(key, typeWeakRef)) { *typeWeakRef = nullptr; return false; @@ -2668,14 +3324,269 @@ namespace Js return true; } - void PathTypeHandler::SetSuccessor(DynamicType * type, const PropertyRecord* propertyRecord, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) + void PathTypeHandler::SetSuccessor(DynamicType * type, const PathTypeSuccessorKey key, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) { if (!propertySuccessors) { Recycler * recycler = scriptContext->GetRecycler(); propertySuccessors = RecyclerNew(recycler, PropertySuccessorsMap, recycler, 3); } - propertySuccessors->Item(propertyRecord->GetPropertyId(), typeWeakRef); + propertySuccessors->Item(key, typeWeakRef); + } + + PathTypeHandlerNoAttr * PathTypeHandlerNoAttr::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + return New(scriptContext, typePath, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + PathTypeHandlerNoAttr * PathTypeHandlerNoAttr::New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + Assert(typePath != nullptr); +#ifdef PROFILE_TYPES + scriptContext->pathTypeHandlerCount++; +#endif + return RecyclerNew(scriptContext->GetRecycler(), PathTypeHandlerNoAttr, typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + PathTypeHandlerNoAttr * PathTypeHandlerNoAttr::New(ScriptContext * scriptContext, PathTypeHandlerNoAttr * typeHandler, bool isLocked, bool isShared) + { + Assert(typeHandler != nullptr); + return RecyclerNew(scriptContext->GetRecycler(), PathTypeHandlerNoAttr, typeHandler->GetTypePath(), typeHandler->GetPathLength(), static_cast(typeHandler->GetSlotCapacity()), typeHandler->GetInlineSlotCapacity(), typeHandler->GetOffsetOfInlineSlots(), isLocked, isShared); + } + + PathTypeHandlerNoAttr::PathTypeHandlerNoAttr(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : + PathTypeHandler(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType) + { + } + + PathTypeHandlerWithAttr * PathTypeHandlerWithAttr::New(ScriptContext * scriptContext, TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + return New(scriptContext, typePath, attributes, accessors, pathLength, max(pathLength, inlineSlotCapacity), inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + PathTypeHandlerWithAttr * PathTypeHandlerWithAttr::New(ScriptContext * scriptContext, TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) + { + Assert(typePath != nullptr); +#ifdef PROFILE_TYPES + scriptContext->simplePathTypeHandlerCount++; +#endif + return RecyclerNew(scriptContext->GetRecycler(), PathTypeHandlerWithAttr, typePath, attributes, accessors, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType); + } + + PathTypeHandlerWithAttr::PathTypeHandlerWithAttr(TypePath *typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked, bool isShared, DynamicType* predecessorType) : + PathTypeHandlerNoAttr(typePath, pathLength, slotCapacity, inlineSlotCapacity, offsetOfInlineSlots, isLocked, isShared, predecessorType), + attributes(attributes), + accessors(accessors) + { + } + + BOOL PathTypeHandlerWithAttr::IsEnumerable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Enumerable); + } + return true; + } + + BOOL PathTypeHandlerWithAttr::IsWritable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Writable); + } + return true; + } + + BOOL PathTypeHandlerWithAttr::IsConfigurable(DynamicObject* instance, PropertyId propertyId) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex != Constants::NoSlot) + { + Assert(attributes); + return (attributes[propertyIndex] & ObjectSlotAttr_Configurable); + } + return true; + } + + BOOL PathTypeHandlerWithAttr::SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetConfigurable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Configurable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Configurable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + + BOOL PathTypeHandlerWithAttr::SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetEnumerable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Enumerable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Enumerable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + + BOOL PathTypeHandlerWithAttr::SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) + { + // Find the property + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + if (!value) + { + // Upgrade type handler if set objectArray item attribute. + // Only check numeric propertyId if objectArray available. + if (instance->HasObjectArray()) + { + PropertyRecord const* propertyRecord = instance->GetScriptContext()->GetPropertyName(propertyId); + if (propertyRecord->IsNumeric()) + { + return ConvertToTypeWithItemAttributes(instance)->SetWritable(instance, propertyId, value); + } + } + } + return true; + } + + ObjectSlotAttributes attr = + (ObjectSlotAttributes)(value ? (attributes[propertyIndex] | ObjectSlotAttr_Writable) : (attributes[propertyIndex] & ~ObjectSlotAttr_Writable)); + return SetAttributesHelper(instance, propertyId, propertyIndex, attributes, attr); + } + +#if ENABLE_NATIVE_CODEGEN + bool PathTypeHandlerWithAttr::IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) + { + return IsObjTypeSpecEquivalentHelper(type, attributes, record, failedPropertyIndex); + } + + bool PathTypeHandlerWithAttr::IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry *entry) + { + return IsObjTypeSpecEquivalentHelper(type, attributes, entry); + } +#endif + + DescriptorFlags PathTypeHandlerWithAttr::GetSetter(DynamicObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyIndex propertyIndex = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (propertyIndex == Constants::NoSlot) + { + return __super::GetSetter(instance, propertyId, setterValue, info, requestContext); + } + ObjectSlotAttributes attr = attributes[propertyIndex]; + if (attr & ObjectSlotAttr_Deleted) + { + return None; + } + + Assert(!(attr & ObjectSlotAttr_Accessor)); + + if (attr & ObjectSlotAttr_Writable) + { + return WritableData; + } + return Data; + } + + DescriptorFlags PathTypeHandlerWithAttr::GetSetter(DynamicObject* instance, JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyRecord const* propertyRecord; + instance->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString, &propertyRecord); + return this->GetSetter(instance, propertyRecord->GetPropertyId(), setterValue, info, requestContext); + } + + BOOL PathTypeHandlerWithAttr::GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) + { + PropertyIndex index = GetTypePath()->LookupInline(propertyId, GetPathLength()); + if (index == Constants::NoSlot) + { + return __super::GetProperty(instance, originalInstance, propertyId, value, info, requestContext); + } + + *value = instance->GetSlot(index); + PropertyValueInfo::Set(info, instance, index, ObjectSlotAttributesToPropertyAttributes(attributes[index])); +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + if (FixPropsOnPathTypes() && (index >= this->GetTypePath()->GetMaxInitializedLength() || this->GetTypePath()->GetIsFixedFieldAt(index, GetPathLength()))) + { + PropertyValueInfo::DisableStoreFieldCache(info); + } +#endif + return true; + } + + BOOL PathTypeHandlerWithAttr::GetProperty(DynamicObject* instance, Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) + { + // Consider: Implement actual string hash lookup + Assert(requestContext); + PropertyRecord const* propertyRecord; + char16 const * propertyName = propertyNameString->GetString(); + charcount_t const propertyNameLength = propertyNameString->GetLength(); + + if (instance->HasObjectArray()) + { + requestContext->GetOrAddPropertyRecord(propertyName, propertyNameLength, &propertyRecord); + } + else + { + requestContext->FindPropertyRecord(propertyName, propertyNameLength, &propertyRecord); + if (propertyRecord == nullptr) + { + *value = requestContext->GetMissingPropertyResult(); + return false; + } + } + return PathTypeHandlerWithAttr::GetProperty(instance, originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext); + } + + BOOL PathTypeHandlerWithAttr::GetAttributesWithPropertyIndex(DynamicObject * instance, PropertyId propertyId, BigPropertyIndex index, PropertyAttributes * attributes) + { + if (index < this->GetPathLength()) + { + Assert(this->GetPropertyId(instance->GetScriptContext(), index) == propertyId); + *attributes = ObjectSlotAttributesToPropertyAttributes(this->attributes[index]); + return true; + } + return false; } #if DBG_DUMP diff --git a/lib/Runtime/Types/PathTypeHandler.h b/lib/Runtime/Types/PathTypeHandler.h index 98de98eda75..306c7bf8399 100644 --- a/lib/Runtime/Types/PathTypeHandler.h +++ b/lib/Runtime/Types/PathTypeHandler.h @@ -6,16 +6,62 @@ namespace Js { + typedef uint8 SetterSlotIndex; + static const SetterSlotIndex NoSetterSlot = (SetterSlotIndex)-1; + + static const uint ObjectSlotAttr_BitSize = 8; + typedef uint8 ObjectSlotAttr_TSize; + + enum ObjectSlotAttributes : ObjectSlotAttr_TSize + { + ObjectSlotAttr_None = 0x00, + ObjectSlotAttr_Enumerable = 0x01, + ObjectSlotAttr_Configurable = 0x02, + ObjectSlotAttr_Writable = 0x04, + ObjectSlotAttr_Deleted = 0x08, + ObjectSlotAttr_Accessor = 0x10, + ObjectSlotAttr_Int = 0x20, + ObjectSlotAttr_Double = 0x40, + ObjectSlotAttr_Default = (ObjectSlotAttr_Writable|ObjectSlotAttr_Enumerable|ObjectSlotAttr_Configurable), + ObjectSlotAttr_PropertyAttributesMask = (ObjectSlotAttr_Default|ObjectSlotAttr_Deleted), + }; + + class PathTypeSuccessorKey + { + private: + PropertyId propertyId; + ObjectSlotAttributes attributes; + + public: + PathTypeSuccessorKey(); + PathTypeSuccessorKey(const PropertyId propertyId, const ObjectSlotAttributes attributes); + + public: + bool HasInfo() const; + void Clear(); + PropertyId GetPropertyId() const; + ObjectSlotAttributes GetAttributes() const; + + public: + bool operator ==(const PathTypeSuccessorKey &other) const; + bool operator !=(const PathTypeSuccessorKey &other) const; + hash_t GetHashCode() const; + operator hash_t() const { return GetHashCode(); } + }; + class SimplePathTypeHandler; - class PathTypeHandlerBase : public DynamicTypeHandler + class PathTypeHandlerBase abstract : public DynamicTypeHandler { + friend class SimplePathTypeHandlerNoAttr; + friend class SimplePathTypeHandlerWithAttr; + friend class PathTypeHandlerNoAttr; + friend class PathTypeHandlerWithAttr; friend class DynamicObject; - friend class SimplePathTypeHandler; - friend class PathTypeHandler; - private: - Field(TypePath*) typePath; + + protected: Field(DynamicType*) predecessorType; // Strong reference to predecessor type so that predecessor types remain in the cache even though they might not be used + Field(TypePath*) typePath; public: DEFINE_GETCPPNAME(); @@ -30,6 +76,9 @@ namespace Js virtual BOOL IsLockable() const override { return true; } virtual BOOL IsSharable() const override { return true; } + static PropertyAttributes ObjectSlotAttributesToPropertyAttributes(const ObjectSlotAttributes attr) { return attr & ObjectSlotAttr_PropertyAttributesMask; } + static ObjectSlotAttributes PropertyAttributesToObjectSlotAttributes(const PropertyAttributes attr) { return (ObjectSlotAttributes)(attr & ObjectSlotAttr_PropertyAttributesMask); } + static bool ObjectSlotAttributesContains(const PropertyAttributes attr) { return attr == (attr & ObjectSlotAttr_PropertyAttributesMask); } static bool UsePathTypeHandlerForObjectLiteral(const PropertyIdArray *const propIds, bool *const check__proto__Ref = nullptr); static DynamicType* CreateTypeForNewScObject(ScriptContext* scriptContext, DynamicType* type, const Js::PropertyIdArray *propIds, bool shareType); static DynamicType* CreateNewScopeObject(ScriptContext* scriptContext, DynamicType* type, const Js::PropertyIdArray *propIds, PropertyAttributes extraAttributes = PropertyNone, uint extraAttributesSlotCount = UINT_MAX); @@ -45,8 +94,6 @@ namespace Js virtual bool IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) override; virtual bool IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry* entry) override; #endif - virtual BOOL FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyString, - PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) override; virtual BOOL HasProperty(DynamicObject* instance, PropertyId propertyId, __out_opt bool *noRedecl = nullptr) override; virtual BOOL HasProperty(DynamicObject* instance, JavascriptString* propertyNameString) override; virtual BOOL GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; @@ -72,7 +119,7 @@ namespace Js virtual void SetAllPropertiesToUndefined(DynamicObject* instance, bool invalidateFixedFields) override; virtual void MarshalAllPropertiesToScriptContext(DynamicObject* instance, ScriptContext* targetScriptContext, bool invalidateFixedFields) override; virtual DynamicTypeHandler* ConvertToTypeWithItemAttributes(DynamicObject* instance) override; - virtual BOOL AllPropertiesAreEnumerable() sealed { return true; } + virtual BOOL AllPropertiesAreEnumerable() override { return true; } virtual BOOL IsPathTypeHandler() const { return TRUE; } virtual void ShrinkSlotAndInlineSlotCapacity() override; @@ -86,6 +133,14 @@ namespace Js virtual void SetIsPrototype(DynamicObject* instance) override; + BOOL FindNextPropertyHelper(ScriptContext* scriptContext, ObjectSlotAttributes * objectAttributes, PropertyIndex& index, JavascriptString** propertyString, + PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info); + BOOL SetAttributesHelper(DynamicObject* instance, PropertyId propertyId, PropertyIndex propertyIndex, ObjectSlotAttributes * instanceAttributes, ObjectSlotAttributes propertyAttributes); +#if ENABLE_NATIVE_CODEGEN + bool IsObjTypeSpecEquivalentHelper(const Type* type, const ObjectSlotAttributes * attributes, const TypeEquivalenceRecord& record, uint& failedPropertyIndex); + bool IsObjTypeSpecEquivalentHelper(const Type* type, const ObjectSlotAttributes * attributes, const EquivalentPropertyEntry* entry); +#endif + #if DBG virtual bool SupportsPrototypeInstances() const { return !IsolatePrototypes(); } virtual bool CanStorePropertyValueDirectly(const DynamicObject* instance, PropertyId propertyId, bool allowLetConst) override; @@ -146,7 +201,7 @@ namespace Js bool HasSingletonInstanceOnlyIfNeeded() const { #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES - return AreSingletonInstancesNeeded() || !this->typePath->HasSingletonInstance(); + return AreSingletonInstancesNeeded() || !this->GetTypePath()->HasSingletonInstance(); #else return true; #endif @@ -165,7 +220,7 @@ namespace Js template T* ConvertToTypeHandler(DynamicObject* instance); - DynamicType* PromoteType(DynamicObject* instance, const PropertyRecord* propertyId, PropertyIndex* propertyIndex); + DynamicType* PromoteType(DynamicObject* instance, const PathTypeSuccessorKey key, PropertyIndex* propertyIndex); DictionaryTypeHandler* ConvertToDictionaryType(DynamicObject* instance); ES5ArrayTypeHandler* ConvertToES5ArrayType(DynamicObject* instance); @@ -178,9 +233,9 @@ namespace Js return ConvertToSimpleDictionaryType(instance, propertyCapacity, mayBecomeShared); } - BOOL AddPropertyInternal(DynamicObject * instance, PropertyId propertyId, Js::Var value, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); + BOOL AddPropertyInternal(DynamicObject * instance, PropertyId propertyId, Js::Var value, ObjectSlotAttributes attr, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); BOOL AddProperty(DynamicObject* instance, PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); - BOOL SetPropertyInternal(DynamicObject* instance, PropertyId propertyId, Var value, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); + template BOOL SetPropertyInternal(DynamicObject* instance, PropertyId propertyId, Var value, ObjectSlotAttributes attr, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); virtual BOOL FreezeImpl(DynamicObject* instance, bool isConvertedType) override; // Checks whether conversion to shared type is needed and performs it, then calls actual operation on the shared type. @@ -190,20 +245,38 @@ namespace Js BOOL ConvertToSharedNonExtensibleTypeIfNeededAndCallOperation(DynamicObject* instance, const PropertyRecord* operationInternalPropertyId, FType operation); template - DynamicType* PromoteType(DynamicType* type, const PropertyRecord* propertyId, bool shareType, ScriptContext* scriptContext, DynamicObject* object = nullptr, PropertyIndex* propertyIndex = nullptr); + DynamicType* PromoteType(DynamicType* type, const PathTypeSuccessorKey key, bool shareType, ScriptContext* scriptContext, DynamicObject* object = nullptr, PropertyIndex* propertyIndex = nullptr); + ObjectSlotAttributes * UpdateAttributes(Recycler * recycler, ObjectSlotAttributes * oldAttributes, uint8 oldPathSize, TypePath * newTypePath); + SetterSlotIndex * UpdateSetterSlots(Recycler * recycler, SetterSlotIndex * oldSetters, uint8 oldPathSize, TypePath * newTypePath); PropertyIndex GetPropertyIndex(PropertyId propertyId); void SetSlotAndCache(DynamicObject* instance, PropertyId propertyId, PropertyRecord const * record, PropertyIndex index, Var value, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects); protected: - virtual bool GetSuccessor(const PropertyRecord* propertyRecord, RecyclerWeakReference ** typeWeakRef) = 0; - virtual void SetSuccessor(DynamicType * type, const PropertyRecord* propertyRecord, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) = 0; + virtual bool GetSuccessor(const PathTypeSuccessorKey successorKey, RecyclerWeakReference ** typeWeakRef) = 0; + virtual void SetSuccessor(DynamicType * type, const PathTypeSuccessorKey successorKey, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) = 0; uint16 GetPathLength() const { return GetUnusedBytesValue(); } TypePath * GetTypePath() const { return typePath; } + virtual void SetTypePath(TypePath *typePath) { this->typePath = typePath; } DynamicType * GetPredecessorType() const { return predecessorType; } PathTypeHandlerBase* GetRootPathTypeHandler(); + virtual ObjectSlotAttributes * GetAttributeArray() const { return nullptr; } + virtual ObjectSlotAttributes GetAttributeArray(const PropertyIndex index) const { return ObjectSlotAttr_Default; } + virtual void SetAttributeArray(ObjectSlotAttributes * attributes) { Assert(false); } + virtual SetterSlotIndex * GetSetterSlots() const { return nullptr; } + virtual SetterSlotIndex GetSetterSlots(const PropertyIndex index) const { return NoSetterSlot; } + virtual void SetSetterSlots(SetterSlotIndex * setters) { Assert(false); } + +#if ENABLE_FIXED_FIELDS +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + template void InitializePath(DynamicObject *const object, const PropertyIndex slotIndex, const PropertyIndex objectSlotCount, ScriptContext *const scriptContext, const FMarkAsFixed MarkAsFixed); + template void InitializeNewPath(DynamicObject *const object, const PropertyIndex slotIndex, const PropertyIndex objectSlotCount, const FMarkAsFixed MarkAsFixed); + void InitializeExistingPath(const PropertyIndex slotIndex, const PropertyIndex objectSlotCount, ScriptContext *const scriptContext); +#endif +#endif + public: virtual void ShrinkSlotAndInlineSlotCapacity(uint16 newInlineSlotCapacity) = 0; virtual bool GetMaxPathLength(uint16 * maxPathLength) = 0; @@ -222,25 +295,89 @@ namespace Js #endif }; +#if ENABLE_FIXED_FIELDS +#ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES + + template + void PathTypeHandlerBase::InitializePath( + DynamicObject *const object, + const PropertyIndex slotIndex, + const PropertyIndex objectSlotCount, + ScriptContext *const scriptContext, + const FMarkAsFixed MarkAsFixed) + { + Assert(slotIndex < objectSlotCount); + Assert(objectSlotCount == slotIndex + 1); + + if(!PathTypeHandler::FixPropsOnPathTypes()) + { + return; + } + + if(objectSlotCount <= GetTypePath()->GetMaxInitializedLength()) + { + InitializeExistingPath(slotIndex, objectSlotCount, scriptContext); + return; + } + InitializeNewPath(object, slotIndex, objectSlotCount, MarkAsFixed); + } + + template + void PathTypeHandlerBase::InitializeNewPath( + DynamicObject *const object, + const PropertyIndex slotIndex, + const PropertyIndex objectSlotCount, + const FMarkAsFixed MarkAsFixed) + { + TypePath *const typePath = GetTypePath(); + Assert(slotIndex == typePath->GetMaxInitializedLength()); + Assert(objectSlotCount > typePath->GetMaxInitializedLength()); + + // We are a adding a property where no instance property has been set before. We rely on properties being + // added in order of indexes to be sure that we don't leave any uninitialized properties interspersed with + // initialized ones, which could lead to incorrect behavior. See comment in TypePath::Branch. + + if(!object) + { + typePath->AddBlankFieldAt(slotIndex, objectSlotCount); + return; + } + + // Consider: It would be nice to assert the slot is actually null. However, we sometimes pre-initialize to + // undefined or even some other special illegal value (for let or const, currently == null) + // Assert(object->GetSlot(index) == null); + + if(PathTypeHandler::ShouldFixAnyProperties() && PathTypeHandler::CanBeSingletonInstance(object)) + { + typePath->AddSingletonInstanceFieldAt(object, slotIndex, MarkAsFixed(), objectSlotCount); + return; + } + + typePath->AddSingletonInstanceFieldAt(slotIndex, objectSlotCount); + } + +#endif +#endif + typedef SimpleDictionaryTypeHandlerBase SimpleDictionaryTypeHandlerWithNontExtensibleSupport; - class SimplePathTypeHandler sealed : public PathTypeHandlerBase + class SimplePathTypeHandler abstract : public PathTypeHandlerBase { private: - Field(const PropertyRecord *) successorPropertyRecord; + Field(PathTypeSuccessorKey) successorKey; Field(RecyclerWeakReference *) successorTypeWeakRef; public: DEFINE_GETCPPNAME(); - private: - SimplePathTypeHandler(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + protected: + SimplePathTypeHandler(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); - DEFINE_VTABLE_CTOR_INIT_NO_REGISTER(SimplePathTypeHandler, PathTypeHandlerBase, successorPropertyRecord(nullptr), successorTypeWeakRef(nullptr)); + DEFINE_VTABLE_CTOR_INIT_NO_REGISTER(SimplePathTypeHandler, PathTypeHandlerBase, successorKey(Constants::NoProperty, ObjectSlotAttr_None), successorTypeWeakRef(nullptr)); protected: - virtual bool GetSuccessor(const PropertyRecord* propertyRecord, RecyclerWeakReference ** typeWeakRef) override; - virtual void SetSuccessor(DynamicType * type, const PropertyRecord* propertyRecord, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) override; + virtual bool GetSuccessor(const PathTypeSuccessorKey successorKey, RecyclerWeakReference ** typeWeakRef) override; + void SetSuccessorHelper(DynamicType * type, const PathTypeSuccessorKey successorKey, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext); public: virtual void ShrinkSlotAndInlineSlotCapacity(uint16 newInlineSlotCapacity) override; @@ -249,34 +386,117 @@ namespace Js virtual void EnsureInlineSlotCapacityIsLocked(bool startFromRoot) override; virtual void VerifyInlineSlotCapacityIsLocked(bool startFromRoot) override; - static SimplePathTypeHandler * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); - static SimplePathTypeHandler * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); - static SimplePathTypeHandler * New(ScriptContext * scriptContext, SimplePathTypeHandler * typeHandler, bool isLocked, bool isShared); #if DBG_DUMP public: void Dump(unsigned indent = 0) const override; #endif }; - class PathTypeHandler sealed : public PathTypeHandlerBase + class SimplePathTypeHandlerNoAttr : public SimplePathTypeHandler + { + public: + DEFINE_GETCPPNAME(); + + protected: + SimplePathTypeHandlerNoAttr(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + + DEFINE_VTABLE_CTOR_NO_REGISTER(SimplePathTypeHandlerNoAttr, SimplePathTypeHandler); + + protected: + virtual void SetSuccessor(DynamicType * type, const PathTypeSuccessorKey successorKey, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) override + { + SetSuccessorHelper(type, successorKey, nullptr, nullptr, typeWeakRef, scriptContext); + } + + public: + static SimplePathTypeHandlerNoAttr * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static SimplePathTypeHandlerNoAttr * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static SimplePathTypeHandlerNoAttr * New(ScriptContext * scriptContext, SimplePathTypeHandlerNoAttr * typeHandler, bool isLocked, bool isShared); + + virtual BOOL FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyString, + PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) override + { + return FindNextPropertyHelper(scriptContext, nullptr, index, propertyString, propertyId, attributes, type, typeToEnumerate, flags, instance, info); + } + }; + + class SimplePathTypeHandlerWithAttr : public SimplePathTypeHandlerNoAttr + { + private: + ObjectSlotAttributes * attributes; + SetterSlotIndex * accessors; + + public: + DEFINE_GETCPPNAME(); + + protected: + SimplePathTypeHandlerWithAttr(TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + + DEFINE_VTABLE_CTOR_INIT_NO_REGISTER(SimplePathTypeHandlerWithAttr, SimplePathTypeHandlerNoAttr, attributes(nullptr), accessors(nullptr)); + + protected: + virtual ObjectSlotAttributes * GetAttributeArray() const override { return attributes; } + virtual ObjectSlotAttributes GetAttributeArray(const PropertyIndex index) const override { Assert(index < GetPathLength()); return attributes[index]; } + virtual void SetAttributeArray(ObjectSlotAttributes * attributes) override { this->attributes = attributes; } + virtual SetterSlotIndex * GetSetterSlots() const override { return accessors; } + virtual SetterSlotIndex GetSetterSlots(const PropertyIndex index) const override { Assert(index < GetPathLength()); return accessors[index]; } + virtual void SetSetterSlots(SetterSlotIndex * accessors) override { this->accessors = accessors; } + + virtual void SetSuccessor(DynamicType * type, const PathTypeSuccessorKey successorKey, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) + { + SetSuccessorHelper(type, successorKey, attributes, accessors, typeWeakRef, scriptContext); + } + + public: + static SimplePathTypeHandlerWithAttr * New(ScriptContext * scriptContext, TypePath * typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static SimplePathTypeHandlerWithAttr * New(ScriptContext * scriptContext, TypePath * typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static SimplePathTypeHandlerWithAttr * New(ScriptContext * scriptContext, SimplePathTypeHandlerWithAttr * typeHandler, bool isLocked, bool isShared); + + virtual BOOL IsEnumerable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL IsWritable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL IsConfigurable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + virtual BOOL SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + virtual BOOL SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + + virtual BOOL GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual BOOL GetProperty(DynamicObject* instance, Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual BOOL GetAttributesWithPropertyIndex(DynamicObject * instance, PropertyId propertyId, BigPropertyIndex index, PropertyAttributes * attributes) override; + + virtual DescriptorFlags GetSetter(DynamicObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual DescriptorFlags GetSetter(DynamicObject* instance, JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; + + virtual BOOL FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyString, + PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) override + { + return FindNextPropertyHelper(scriptContext, this->attributes, index, propertyString, propertyId, attributes, type, typeToEnumerate, flags, instance, info); + } + virtual BOOL AllPropertiesAreEnumerable() sealed override { return false; } +#if ENABLE_NATIVE_CODEGEN + virtual bool IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) override; + virtual bool IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry* entry) override; +#endif + }; + + class PathTypeHandler abstract : public PathTypeHandlerBase { friend class SimplePathTypeHandler; private: - typedef JsUtil::WeakReferenceDictionary> PropertySuccessorsMap; + typedef JsUtil::WeakReferenceDictionary> PropertySuccessorsMap; Field(PropertySuccessorsMap *) propertySuccessors; public: DEFINE_GETCPPNAME(); - private: - PathTypeHandler(TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + protected: + PathTypeHandler(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); DEFINE_VTABLE_CTOR_INIT_NO_REGISTER(PathTypeHandler, PathTypeHandlerBase, propertySuccessors(nullptr)); protected: - virtual bool GetSuccessor(const PropertyRecord* propertyRecord, RecyclerWeakReference ** typeWeakRef) override; - virtual void SetSuccessor(DynamicType * type, const PropertyRecord* propertyRecord, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) override; + virtual bool GetSuccessor(const PathTypeSuccessorKey successorKey, RecyclerWeakReference ** typeWeakRef) override; + virtual void SetSuccessor(DynamicType * type, const PathTypeSuccessorKey successorKey, RecyclerWeakReference * typeWeakRef, ScriptContext * scriptContext) override; public: virtual void ShrinkSlotAndInlineSlotCapacity(uint16 newInlineSlotCapacity) override; @@ -285,12 +505,85 @@ namespace Js virtual void EnsureInlineSlotCapacityIsLocked(bool startFromRoot) override; virtual void VerifyInlineSlotCapacityIsLocked(bool startFromRoot) override; - static PathTypeHandler * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); - static PathTypeHandler * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); - static PathTypeHandler * New(ScriptContext * scriptContext, PathTypeHandler * typeHandler, bool isLocked, bool isShared); #if DBG_DUMP public: void Dump(unsigned indent = 0) const override; #endif }; + + class PathTypeHandlerNoAttr : public PathTypeHandler + { + public: + DEFINE_GETCPPNAME(); + + protected: + PathTypeHandlerNoAttr(TypePath *typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + + DEFINE_VTABLE_CTOR_NO_REGISTER(PathTypeHandlerNoAttr, PathTypeHandler); + + public: + static PathTypeHandlerNoAttr * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static PathTypeHandlerNoAttr * New(ScriptContext * scriptContext, TypePath* typePath, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static PathTypeHandlerNoAttr * New(ScriptContext * scriptContext, PathTypeHandlerNoAttr * typeHandler, bool isLocked, bool isShared); + + virtual BOOL FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyString, + PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) override + { + return FindNextPropertyHelper(scriptContext, nullptr, index, propertyString, propertyId, attributes, type, typeToEnumerate, flags, instance, info); + } + }; + + class PathTypeHandlerWithAttr : public PathTypeHandlerNoAttr + { + private: + ObjectSlotAttributes * attributes; + SetterSlotIndex * accessors; + + public: + DEFINE_GETCPPNAME(); + + protected: + PathTypeHandlerWithAttr(TypePath* typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + + DEFINE_VTABLE_CTOR_INIT_NO_REGISTER(PathTypeHandlerWithAttr, PathTypeHandlerNoAttr, attributes(nullptr), accessors(nullptr)); + + protected: + virtual ObjectSlotAttributes * GetAttributeArray() const override { return attributes; } + virtual ObjectSlotAttributes GetAttributeArray(const PropertyIndex index) const override { Assert(index < GetPathLength()); return attributes[index]; } + virtual void SetAttributeArray(ObjectSlotAttributes * attributes) override { this->attributes = attributes; } + virtual SetterSlotIndex * GetSetterSlots() const override { return accessors; } + virtual SetterSlotIndex GetSetterSlots(const PropertyIndex index) const override { Assert(index < GetPathLength()); return accessors[index]; } + virtual void SetSetterSlots(SetterSlotIndex * accessors) override { this->accessors = accessors; } + + public: + static PathTypeHandlerWithAttr * New(ScriptContext * scriptContext, TypePath * typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static PathTypeHandlerWithAttr * New(ScriptContext * scriptContext, TypePath * typePath, ObjectSlotAttributes * attributes, SetterSlotIndex * accessors, uint16 pathLength, const PropertyIndex slotCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots, bool isLocked = false, bool isShared = false, DynamicType* predecessorType = nullptr); + static PathTypeHandlerWithAttr * New(ScriptContext * scriptContext, PathTypeHandlerWithAttr * typeHandler, bool isLocked, bool isShared); + + virtual BOOL IsEnumerable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL IsWritable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL IsConfigurable(DynamicObject* instance, PropertyId propertyId) override; + virtual BOOL SetEnumerable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + virtual BOOL SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + virtual BOOL SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) override; + + virtual BOOL GetProperty(DynamicObject* instance, Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual BOOL GetProperty(DynamicObject* instance, Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual BOOL GetAttributesWithPropertyIndex(DynamicObject * instance, PropertyId propertyId, BigPropertyIndex index, PropertyAttributes * attributes) override; + + virtual DescriptorFlags GetSetter(DynamicObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; + virtual DescriptorFlags GetSetter(DynamicObject* instance, JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; + + virtual BOOL FindNextProperty(ScriptContext* scriptContext, PropertyIndex& index, JavascriptString** propertyString, + PropertyId* propertyId, PropertyAttributes* attributes, Type* type, DynamicType *typeToEnumerate, EnumeratorFlags flags, DynamicObject* instance, PropertyValueInfo* info) override + { + return FindNextPropertyHelper(scriptContext, this->attributes, index, propertyString, propertyId, attributes, type, typeToEnumerate, flags, instance, info); + } + virtual BOOL AllPropertiesAreEnumerable() sealed override { return false; } +#if ENABLE_NATIVE_CODEGEN + virtual bool IsObjTypeSpecEquivalent(const Type* type, const TypeEquivalenceRecord& record, uint& failedPropertyIndex) override; + virtual bool IsObjTypeSpecEquivalent(const Type* type, const EquivalentPropertyEntry* entry) override; +#endif + }; + } diff --git a/lib/Runtime/Types/TypePath.h b/lib/Runtime/Types/TypePath.h index df40bbfd6a7..ebf26808b9d 100644 --- a/lib/Runtime/Types/TypePath.h +++ b/lib/Runtime/Types/TypePath.h @@ -57,6 +57,9 @@ namespace Js class TypePath { friend class PathTypeHandlerBase; + friend class SimplePathTypeHandlerWithAttr; + friend class PathTypeHandlerWithAttr; + public: // This is the space between the end of the TypePath and the allocation granularity that can be used for assignments too. #ifdef SUPPORT_FIXED_FIELDS_ON_PATH_TYPES