2
0
Эх сурвалжийг харах

Share types of CrossSiteObject<ScriptFunction> objects

Paul Leathers 7 жил өмнө
parent
commit
61b61fd8d9

+ 1 - 0
lib/Common/ConfigFlagsList.h

@@ -388,6 +388,7 @@ PHASE(All)
         PHASE(ShareTypesWithAttributes)
         PHASE(ShareAccessorTypes)
         PHASE(ShareFuncTypes)
+        PHASE(ShareCrossSiteFuncTypes)
         PHASE(ConditionalCompilation)
         PHASE(InterpreterProfile)
         PHASE(InterpreterAutoProfile)

+ 43 - 1
lib/Runtime/Base/FunctionBody.cpp

@@ -1484,6 +1484,8 @@ namespace Js
 
 #define CopyDeferParseField(field) other->field = this->field;
         CopyDeferParseField(flags);
+        CopyDeferParseField(crossSiteDeferredFunctionType);
+        CopyDeferParseField(crossSiteUndeferredFunctionType);
         CopyDeferParseField(m_isDeclaration);
         CopyDeferParseField(m_isAccessor);
         CopyDeferParseField(m_isStrictMode);
@@ -1598,6 +1600,8 @@ namespace Js
         LocalFunctionId functionId, Utf8SourceInfo* sourceInfo, ScriptContext* scriptContext, uint functionNumber,
         const char16* displayName, uint displayNameLength, uint displayShortNameOffset, FunctionInfo::Attributes attributes, FunctionBodyFlags flags) :
       FunctionProxy(scriptContext, sourceInfo, functionNumber),
+      crossSiteDeferredFunctionType(nullptr),
+      crossSiteUndeferredFunctionType(nullptr),
 #if DYNAMIC_INTERPRETER_THUNK
       m_dynamicInterpreterThunk(nullptr),
 #endif
@@ -2085,6 +2089,28 @@ namespace Js
         undeferredFunctionType = type;
     }
 
+    ScriptFunctionType * FunctionProxy::GetCrossSiteDeferredFunctionType() const
+    {
+        return HasParseableInfo() ? GetParseableFunctionInfo()->GetCrossSiteDeferredFunctionType() : nullptr;
+    }
+
+    void FunctionProxy::SetCrossSiteDeferredFunctionType(ScriptFunctionType * type)
+    {
+        Assert(HasParseableInfo());
+        GetParseableFunctionInfo()->SetCrossSiteDeferredFunctionType(type);
+    }
+
+    ScriptFunctionType * FunctionProxy::GetCrossSiteUndeferredFunctionType() const
+    {
+        return HasParseableInfo() ? GetParseableFunctionInfo()->GetCrossSiteUndeferredFunctionType() : nullptr;
+    }
+
+    void FunctionProxy::SetCrossSiteUndeferredFunctionType(ScriptFunctionType * type)
+    {
+        Assert(HasParseableInfo());
+        GetParseableFunctionInfo()->SetCrossSiteUndeferredFunctionType(type);
+    }
+
     JavascriptMethod FunctionProxy::GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const
     {
         Assert(entryPoint->jsMethod != nullptr);
@@ -4984,6 +5010,14 @@ namespace Js
             this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
             this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
         }
+        if (this->crossSiteDeferredFunctionType)
+        {
+            this->crossSiteDeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
+        }
+        if (this->crossSiteUndeferredFunctionType)
+        {
+            this->crossSiteUndeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
+        }
 
 #if DBG
         if (!this->HasValidEntryPoint())
@@ -5275,7 +5309,7 @@ namespace Js
         if (this->deferredPrototypeType)
         {
             // Update old entry points on the deferred prototype type,
-            // as they may point to old native code gen regions which age gone now.
+            // as they may point to old native code gen regions which are gone now.
             this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
             this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
         }
@@ -5284,6 +5318,14 @@ namespace Js
             this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
             this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
         }
+        if (this->crossSiteDeferredFunctionType)
+        {
+            this->crossSiteDeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
+        }
+        if (this->crossSiteUndeferredFunctionType)
+        {
+            this->crossSiteUndeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
+        }
         ReinitializeExecutionModeAndLimits();
     }
 

+ 20 - 0
lib/Runtime/Base/FunctionBody.h

@@ -1022,6 +1022,7 @@ namespace Js
         void SetEnclosedByGlobalFunc();
         bool CanBeDeferred() const;
         BOOL IsDeferredDeserializeFunction() const;
+        BOOL HasParseableInfo() const;
         BOOL IsDeferredParseFunction() const;
         FunctionInfo::Attributes GetAttributes() const;
         void SetAttributes(FunctionInfo::Attributes attributes);
@@ -1057,6 +1058,10 @@ namespace Js
         ScriptFunctionType * EnsureDeferredPrototypeType();
         ScriptFunctionType * GetUndeferredFunctionType() const;
         void SetUndeferredFunctionType(ScriptFunctionType * type);
+        ScriptFunctionType * GetCrossSiteDeferredFunctionType() const;
+        void SetCrossSiteDeferredFunctionType(ScriptFunctionType * type);
+        ScriptFunctionType * GetCrossSiteUndeferredFunctionType() const;
+        void SetCrossSiteUndeferredFunctionType(ScriptFunctionType * type);
         JavascriptMethod GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const;
 
         // Function object type list methods
@@ -1302,6 +1307,13 @@ namespace Js
         return GetFunctionInfo()->IsDeferredDeserializeFunction();
     }
 
+    inline BOOL FunctionProxy::HasParseableInfo() const
+    {
+        Assert(GetFunctionInfo());
+        Assert(GetFunctionInfo()->GetFunctionProxy() == this);
+        return GetFunctionInfo()->HasParseableInfo();
+    }
+
     inline BOOL FunctionProxy::IsDeferredParseFunction() const
     {
         Assert(GetFunctionInfo());
@@ -1578,6 +1590,11 @@ namespace Js
         uint32 GetGrfscr() const;
         void SetGrfscr(uint32 grfscr);
 
+        ScriptFunctionType * GetCrossSiteDeferredFunctionType() const { return crossSiteDeferredFunctionType; }
+        void SetCrossSiteDeferredFunctionType(ScriptFunctionType * type) { Assert(!crossSiteDeferredFunctionType); crossSiteDeferredFunctionType = type; }
+        ScriptFunctionType * GetCrossSiteUndeferredFunctionType() const { return crossSiteUndeferredFunctionType; }
+        void SetCrossSiteUndeferredFunctionType(ScriptFunctionType * type) { Assert(!crossSiteUndeferredFunctionType); crossSiteUndeferredFunctionType = type; }
+
         ///----------------------------------------------------------------------------
         ///
         /// ParseableFunctionInfo::GetInParamsCount
@@ -1807,6 +1824,9 @@ namespace Js
 #if DYNAMIC_INTERPRETER_THUNK
         FieldNoBarrier(void*) m_dynamicInterpreterThunk;  // Unique 'thunk' for every interpreted function - used for ETW symbol decoding.
 #endif
+        FieldWithBarrier(ScriptFunctionType*) crossSiteDeferredFunctionType;
+        FieldWithBarrier(ScriptFunctionType*) crossSiteUndeferredFunctionType;
+
         FieldWithBarrier(uint) m_cbStartOffset;         // pUtf8Source is this many bytes from the start of the scriptContext's source buffer.
                                                         // This is generally the same as m_cchStartOffset unless the buffer has a BOM or other non-ascii characters
         FieldWithBarrier(uint) m_cbStartPrintOffset;    // pUtf8Source is this many bytes from the start of the toString-relevant part of the scriptContext's source buffer.

+ 91 - 15
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -583,7 +583,7 @@ namespace Js
         functionWithPrototypeTypeHandler->SetHasKnownSlot0();
 
         externalFunctionWithDeferredPrototypeType = CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptExternalFunction::ExternalFunctionThunk, true /*isShared*/);
-        externalFunctionWithLengthAndDeferredPrototypeType = CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptExternalFunction::ExternalFunctionThunk, true /*isShared*/, /* isLengthAvailable */ true);
+        externalFunctionWithLengthAndDeferredPrototypeType = CreateDeferredLengthPrototypeFunctionTypeNoProfileThunk(JavascriptExternalFunction::ExternalFunctionThunk, true /*isShared*/);
         wrappedFunctionWithDeferredPrototypeType = CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptExternalFunction::WrappedFunctionThunk, true /*isShared*/);
         stdCallFunctionWithDeferredPrototypeType = CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptExternalFunction::StdCallExternalFunctionThunk, true /*isShared*/);
         idMappedFunctionWithPrototypeType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, JavascriptExternalFunction::ExternalFunctionThunk,
@@ -595,6 +595,8 @@ namespace Js
 
         boundFunctionType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, BoundFunction::NewInstance,
             GetDeferredFunctionTypeHandler(), true, true);
+        crossSiteDeferredFunctionType = CreateDeferredFunctionTypeNoProfileThunk(
+            scriptContext->CurrentCrossSiteThunk, true /*isShared*/);
         crossSiteDeferredPrototypeFunctionType = CreateDeferredPrototypeFunctionTypeNoProfileThunk(
             scriptContext->CurrentCrossSiteThunk, true /*isShared*/);
         crossSiteIdMappedFunctionWithPrototypeType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, scriptContext->CurrentCrossSiteThunk,
@@ -1022,20 +1024,51 @@ namespace Js
             isAnonymousFunction ? GetDeferredAnonymousPrototypeAsyncFunctionTypeHandler() : GetDeferredPrototypeAsyncFunctionTypeHandler(scriptContext), isShared, isShared);
     }
 
+    DynamicType * JavascriptLibrary::CreateDeferredFunctionType(JavascriptMethod entrypoint)
+    {
+        return CreateDeferredFunctionTypeNoProfileThunk(this->inDispatchProfileMode ? ProfileEntryThunk : entrypoint);
+    }
+
     DynamicType * JavascriptLibrary::CreateDeferredPrototypeFunctionType(JavascriptMethod entrypoint)
     {
         return CreateDeferredPrototypeFunctionTypeNoProfileThunk(this->inDispatchProfileMode ? ProfileEntryThunk : entrypoint);
     }
 
-    DynamicType * JavascriptLibrary::CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared, bool isLengthAvailable)
+    DynamicType * JavascriptLibrary::CreateDeferredFunctionTypeNoProfileThunk(JavascriptMethod entryPoint, bool isShared)
+    {
+        return CreateDeferredFunctionTypeNoProfileThunk_Internal<false, false>(entryPoint, isShared);
+    }
+
+    DynamicType * JavascriptLibrary::CreateDeferredLengthFunctionTypeNoProfileThunk(JavascriptMethod entryPoint, bool isShared)
+    {
+        return CreateDeferredFunctionTypeNoProfileThunk_Internal<true, false>(entryPoint, isShared);
+    }
+
+    DynamicType * JavascriptLibrary::CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entryPoint, bool isShared)
+    {
+        return CreateDeferredFunctionTypeNoProfileThunk_Internal<false, true>(entryPoint, isShared);
+    }
+
+    DynamicType * JavascriptLibrary::CreateDeferredLengthPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entryPoint, bool isShared)
+    {
+        return CreateDeferredFunctionTypeNoProfileThunk_Internal<true, true>(entryPoint, isShared);
+    }
+
+    template<bool isLengthAvailable, bool isPrototypeAvailable>
+    DynamicType * JavascriptLibrary::CreateDeferredFunctionTypeNoProfileThunk_Internal(JavascriptMethod entrypoint, bool isShared)
     {
         // Note: the lack of TypeHandler switching here based on the isAnonymousFunction flag is intentional.
         // We can't switch shared typeHandlers and RuntimeFunctions do not produce script code for us to know if a function is Anonymous.
         // As a result we may have an issue where hasProperty would say you have a name property but getProperty returns undefined
-        return DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, entrypoint,
-            isLengthAvailable ? GetDeferredPrototypeFunctionWithLengthTypeHandler(scriptContext) : GetDeferredPrototypeFunctionTypeHandler(scriptContext),
-            isShared, isShared);
+        DynamicTypeHandler * typeHandler = 
+            isLengthAvailable ?
+                (isPrototypeAvailable ?
+                    GetDeferredPrototypeFunctionWithLengthTypeHandler(scriptContext) : GetDeferredFunctionWithLengthTypeHandler()) :
+                (isPrototypeAvailable ?
+                    GetDeferredPrototypeFunctionTypeHandler(scriptContext) : GetDeferredFunctionTypeHandler());
+        return DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, entrypoint, typeHandler, isShared, isShared);
     }
+
     DynamicType * JavascriptLibrary::CreateFunctionType(JavascriptMethod entrypoint, RecyclableObject* prototype)
     {
         if (prototype == nullptr)
@@ -5243,11 +5276,7 @@ namespace Js
     {
         Assert(function->GetDynamicType()->GetIsLocked());
 
-        if (VarIs<ScriptFunction>(function))
-        {
-            this->SetCrossSiteForLockedNonBuiltInFunctionType(function);
-        }
-        else if (VarIs<BoundFunction>(function))
+        if (VarIs<ScriptFunction>(function) || VarIs<BoundFunction>(function))
         {
             this->SetCrossSiteForLockedNonBuiltInFunctionType(function);
         }
@@ -5259,6 +5288,11 @@ namespace Js
             {
                 function->ReplaceType(crossSiteDeferredPrototypeFunctionType);
             }
+            else if (typeHandler == JavascriptLibrary::GetDeferredFunctionTypeHandler()
+                || typeHandler == JavascriptLibrary::GetDeferredFunctionWithLengthTypeHandler())
+            {
+                function->ReplaceType(crossSiteDeferredFunctionType);
+            }
             else if (typeHandler == Js::DeferredTypeHandler<Js::JavascriptExternalFunction::DeferredConstructorInitializer>::GetDefaultInstance())
             {
                 function->ReplaceType(crossSiteExternalConstructFunctionWithPrototypeType);
@@ -5276,16 +5310,58 @@ namespace Js
 
     void JavascriptLibrary::SetCrossSiteForLockedNonBuiltInFunctionType(JavascriptFunction * function)
     {
+        FunctionProxy * functionProxy = function->GetFunctionProxy();
         DynamicTypeHandler *typeHandler = function->GetTypeHandler();
-        if (typeHandler->IsPathTypeHandler())
+        if (typeHandler->IsDeferredTypeHandler())
         {
-            PathTypeHandlerBase::FromTypeHandler(typeHandler)->ConvertToNonShareableTypeHandler(function);
+            if (functionProxy && functionProxy->GetCrossSiteDeferredFunctionType())
+            {
+                function->ReplaceType(functionProxy->GetCrossSiteDeferredFunctionType());
+            }
+            else
+            {
+                function->ChangeType();
+                function->SetEntryPoint(scriptContext->CurrentCrossSiteThunk);
+                if (functionProxy && !PHASE_OFF1(ShareCrossSiteFuncTypesPhase))
+                {
+                    function->ShareType();
+                    functionProxy->SetCrossSiteDeferredFunctionType(UnsafeVarTo<ScriptFunction>(function)->GetScriptFunctionType());
+                }
+            }
         }
-        else
+        else 
         {
-            function->ChangeType();
+            if (functionProxy && functionProxy->GetCrossSiteUndeferredFunctionType())
+            {
+                function->ReplaceType(functionProxy->GetCrossSiteUndeferredFunctionType());
+            }
+            else
+            {
+                if (typeHandler->IsPathTypeHandler())
+                {
+                    if (!PHASE_OFF1(ShareCrossSiteFuncTypesPhase))
+                    {
+                        DynamicType *type = function->DuplicateType();
+                        PathTypeHandlerBase::FromTypeHandler(typeHandler)->BuildPathTypeFromNewRoot(function, &type);
+                        function->ReplaceType(type);
+                    }
+                    else
+                    {
+                        PathTypeHandlerBase::FromTypeHandler(typeHandler)->ConvertToNonShareableTypeHandler(function);
+                    }
+                }
+                else
+                {
+                    function->ChangeType();
+                }
+                function->SetEntryPoint(scriptContext->CurrentCrossSiteThunk);
+                if (functionProxy && function->GetTypeHandler()->GetMayBecomeShared() && !PHASE_OFF1(ShareCrossSiteFuncTypesPhase))
+                {
+                    function->ShareType();
+                    functionProxy->SetCrossSiteUndeferredFunctionType(UnsafeVarTo<ScriptFunction>(function)->GetScriptFunctionType());
+                }
+            }
         }
-        function->SetEntryPoint(scriptContext->CurrentCrossSiteThunk);
     }
 
     JavascriptExternalFunction*

+ 7 - 1
lib/Runtime/Library/JavascriptLibrary.h

@@ -300,6 +300,7 @@ namespace Js
         Field(DynamicType *) defaultExternalConstructorFunctionWithDeferredPrototypeType;
         Field(DynamicType *) boundFunctionType;
         Field(DynamicType *) regexConstructorType;
+        Field(DynamicType *) crossSiteDeferredFunctionType;
         Field(DynamicType *) crossSiteDeferredPrototypeFunctionType;
         Field(DynamicType *) crossSiteIdMappedFunctionWithPrototypeType;
         Field(DynamicType *) crossSiteExternalConstructFunctionWithPrototypeType;
@@ -912,8 +913,13 @@ namespace Js
         template<bool isNameAvailable>
         static DynamicTypeHandler * GetDeferredAsyncFunctionTypeHandlerBase();
 
+        DynamicType * CreateDeferredFunctionType(JavascriptMethod entrypoint);
         DynamicType * CreateDeferredPrototypeFunctionType(JavascriptMethod entrypoint);
-        DynamicType * CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared = false, bool isLengthAvailable = false);
+        DynamicType * CreateDeferredPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared = false);
+        DynamicType * CreateDeferredFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared = false);
+        DynamicType * CreateDeferredLengthPrototypeFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared = false);
+        DynamicType * CreateDeferredLengthFunctionTypeNoProfileThunk(JavascriptMethod entrypoint, bool isShared = false);
+        template<bool isLengthAvailable, bool isPrototypeAvailable> DynamicType * CreateDeferredFunctionTypeNoProfileThunk_Internal(JavascriptMethod entrypoint, bool isShared);
         DynamicType * CreateFunctionType(JavascriptMethod entrypoint, RecyclableObject* prototype = nullptr);
         DynamicType * CreateFunctionWithConfigurableLengthType(FunctionInfo * functionInfo);
         DynamicType * CreateFunctionWithLengthType(FunctionInfo * functionInfo);

+ 21 - 7
lib/Runtime/Types/DeferredTypeHandler.cpp

@@ -20,30 +20,44 @@ namespace Js
         // also responsible for populating PropertyTypes to indicate whether there are any read-only
         // properties unknown to the type handler.
 
-        BOOL isProto = this->GetIsPrototype();
+        bool isProto = this->GetIsPrototype();
+        bool isCrossSite = CrossSite::IsThunk(instance->GetType()->GetEntryPoint()); 
 
         ScriptContext* scriptContext = instance->GetScriptContext();
         instance->EnsureSlots(0, typeHandler->GetSlotCapacity(), scriptContext, typeHandler);
 
         FunctionProxy * functionProxy = instance->GetFunctionProxy();
+
         ScriptFunctionType * undeferredFunctionType = nullptr;
         if (functionProxy)
         {
-            undeferredFunctionType = functionProxy->GetUndeferredFunctionType();
+            undeferredFunctionType = isCrossSite ? functionProxy->GetCrossSiteUndeferredFunctionType() : functionProxy->GetUndeferredFunctionType();
         }
-        if (undeferredFunctionType && !isProto && !instance->IsCrossSiteObject())
+
+        if (undeferredFunctionType && !isProto)
         {
             Assert(undeferredFunctionType->GetIsShared());
-            Assert(!CrossSite::IsThunk(undeferredFunctionType->GetEntryPoint()));
             instance->ReplaceType(undeferredFunctionType);
         }
         else
         {
             typeHandler->SetInstanceTypeHandler(instance);
-            if (functionProxy && !isProto && typeHandler->GetMayBecomeShared() && !CrossSite::IsThunk(instance->GetType()->GetEntryPoint()) && !PHASE_OFF1(ShareFuncTypesPhase))
+            if (functionProxy && !isProto && typeHandler->GetMayBecomeShared() && !PHASE_OFF1(ShareFuncTypesPhase))
             {
-                Assert(!functionProxy->GetUndeferredFunctionType());
-                functionProxy->SetUndeferredFunctionType(UnsafeVarTo<ScriptFunction>(instance)->GetScriptFunctionType());
+                ScriptFunctionType *newType = UnsafeVarTo<ScriptFunction>(instance)->GetScriptFunctionType();
+                if (isCrossSite)
+                {
+                    if (functionProxy->HasParseableInfo())
+                    {
+                        Assert(!functionProxy->GetParseableFunctionInfo()->GetCrossSiteUndeferredFunctionType());
+                        functionProxy->GetParseableFunctionInfo()->SetCrossSiteUndeferredFunctionType(newType);
+                    }
+                }
+                else
+                {
+                    Assert(!functionProxy->GetUndeferredFunctionType());
+                    functionProxy->SetUndeferredFunctionType(newType);
+                }
                 instance->ShareType();
             }
         }

+ 35 - 25
lib/Runtime/Types/PathTypeHandler.cpp

@@ -2674,6 +2674,39 @@ namespace Js
         return clonedTypeHandler;
     }
 
+    PathTypeHandlerBase *
+    PathTypeHandlerBase::BuildPathTypeFromNewRoot(DynamicObject * instance, DynamicType ** typeRef)
+    {
+        Assert(typeRef);
+        DynamicType * type = *typeRef;
+        ScriptContext * scriptContext = type->GetScriptContext();
+
+        PathTypeHandlerBase* newTypeHandler = PathTypeHandlerNoAttr::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, static_cast<PropertyIndex>(this->GetSlotCapacity()), this->GetInlineSlotCapacity(), this->GetOffsetOfInlineSlots(), GetIsLocked(), GetIsShared());
+        newTypeHandler->SetFlags(MayBecomeSharedFlag, GetFlags());
+        type->typeHandler = newTypeHandler;
+
+        // Promote type based on existing properties to get new type which will be cached and shared
+        ObjectSlotAttributes * attributes = this->GetAttributeArray();
+        for (PropertyIndex propertyIndex = 0; propertyIndex < GetPathLength(); propertyIndex++)
+        {
+            Js::PropertyId propertyId = GetPropertyId(scriptContext, propertyIndex);
+            ObjectSlotAttributes attr = attributes ? attributes[propertyIndex] : ObjectSlotAttr_Default;
+            type = newTypeHandler->PromoteType<false>(type, PathTypeSuccessorKey(propertyId, attr), true, scriptContext, instance, &propertyIndex);
+            newTypeHandler = PathTypeHandlerBase::FromTypeHandler(type->GetTypeHandler());
+            if (attr == ObjectSlotAttr_Setter)
+            {
+                newTypeHandler->SetSetterSlot(newTypeHandler->GetTypePath()->LookupInline(propertyId, newTypeHandler->GetPathLength()), (PathTypeSetterSlotIndex)(newTypeHandler->GetPathLength() - 1));
+            }
+        }
+        Assert(newTypeHandler->GetPathLength() == GetPathLength());
+        Assert(newTypeHandler->GetPropertyCount() == GetPropertyCount());
+        Assert(newTypeHandler->GetSetterCount() == GetSetterCount());
+
+        *typeRef = type;
+
+        return newTypeHandler;
+    }
+
     void PathTypeHandlerBase::SetPrototype(DynamicObject* instance, RecyclableObject* newPrototype)
     {
         // No typesharing for ExternalType
@@ -2749,39 +2782,16 @@ namespace Js
 
         if (cachedDynamicType == nullptr)
         {
-            PathTypeHandlerBase* newTypeHandler = PathTypeHandlerNoAttr::New(scriptContext, scriptContext->GetLibrary()->GetRootPath(), 0, static_cast<PropertyIndex>(this->GetSlotCapacity()), this->GetInlineSlotCapacity(), this->GetOffsetOfInlineSlots(), GetIsLocked(), GetIsShared());
-            newTypeHandler->SetFlags(MayBecomeSharedFlag, GetFlags());
-
             cachedDynamicType = instance->DuplicateType();
             cachedDynamicType->SetPrototype(newPrototype);
-            cachedDynamicType->typeHandler = newTypeHandler;
+            this->BuildPathTypeFromNewRoot(instance, &cachedDynamicType);
 
-            // Make type locked, shared only if we are using cache
             if (useCache)
             {
+                // Make type locked, shared only if we are using cache
                 cachedDynamicType->LockType();
                 cachedDynamicType->ShareType();
-            }
-
-            // Promote type based on existing properties to get new type which will be cached and shared
-            ObjectSlotAttributes * attributes = this->GetAttributeArray();
-            for (PropertyIndex propertyIndex = 0; propertyIndex < GetPathLength(); propertyIndex++)
-            {
-                Js::PropertyId propertyId = GetPropertyId(scriptContext, propertyIndex);
-                ObjectSlotAttributes attr = attributes ? attributes[propertyIndex] : ObjectSlotAttr_Default;
-                cachedDynamicType = newTypeHandler->PromoteType<false>(cachedDynamicType, PathTypeSuccessorKey(propertyId, attr), true, scriptContext, instance, &propertyIndex);
-                newTypeHandler = PathTypeHandlerBase::FromTypeHandler(cachedDynamicType->GetTypeHandler());
-                if (attr == ObjectSlotAttr_Setter)
-                {
-                    newTypeHandler->SetSetterSlot(newTypeHandler->GetTypePath()->LookupInline(propertyId, newTypeHandler->GetPathLength()), (PathTypeSetterSlotIndex)(newTypeHandler->GetPathLength() - 1));
-                }
-            }
-            Assert(newTypeHandler->GetPathLength() == GetPathLength());
-            Assert(newTypeHandler->GetPropertyCount() == GetPropertyCount());
-            Assert(newTypeHandler->GetSetterCount() == GetSetterCount());
 
-            if (useCache)
-            {
                 if (oldTypeToPromotedTypeMap == nullptr)
                 {
                     oldTypeToPromotedTypeMap = RecyclerNew(instance->GetRecycler(), TypeTransitionMap, instance->GetRecycler(), 2);

+ 2 - 0
lib/Runtime/Types/PathTypeHandler.h

@@ -189,6 +189,8 @@ namespace Js
 
         virtual void SetIsPrototype(DynamicObject* instance) override;
 
+        PathTypeHandlerBase * BuildPathTypeFromNewRoot(DynamicObject * instance, DynamicType ** type);
+
         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 SetAttributesAtIndex(DynamicObject* instance, PropertyId propertyId, PropertyIndex index, PropertyAttributes attributes);

+ 1 - 3
lib/Runtime/Types/SimpleTypeHandler.cpp

@@ -64,7 +64,7 @@ namespace Js
     template<size_t size>
     bool SimpleTypeHandler<size>::DoConvertToPathType(DynamicType* type)
     {
-        if (CrossSite::IsThunk(type->GetEntryPoint()) || type->GetTypeHandler()->GetIsPrototype())
+        if ((PHASE_ON1(ShareCrossSiteFuncTypesPhase) && CrossSite::IsThunk(type->GetEntryPoint())) || type->GetTypeHandler()->GetIsPrototype())
         {
             return false;
         }
@@ -157,8 +157,6 @@ namespace Js
     template<size_t size>
     PathTypeHandlerBase* SimpleTypeHandler<size>::ConvertToPathType(DynamicObject* instance)
     {
-        Assert(!CrossSite::IsThunk(instance->GetType()->GetEntryPoint()));
-
         ScriptContext *scriptContext = instance->GetScriptContext();
         PathTypeHandlerBase* newTypeHandler =
             PathTypeHandlerNoAttr::New(