diff --git a/lib/Runtime/Base/FunctionBody.cpp b/lib/Runtime/Base/FunctionBody.cpp index adedae0cfad..2d5b1393352 100644 --- a/lib/Runtime/Base/FunctionBody.cpp +++ b/lib/Runtime/Base/FunctionBody.cpp @@ -72,6 +72,8 @@ namespace Js m_isTopLevel(false), m_isPublicLibraryCode(false), m_scriptContext(scriptContext), + deferredPrototypeType(nullptr), + undeferredFunctionType(nullptr), m_utf8SourceInfo(utf8SourceInfo), m_functionNumber(functionNumber), m_defaultEntryPointInfo(nullptr), @@ -2015,6 +2017,16 @@ namespace Js return type; } + ScriptFunctionType * FunctionProxy::GetUndeferredFunctionType() const + { + return undeferredFunctionType; + } + + void FunctionProxy::SetUndeferredFunctionType(ScriptFunctionType * type) + { + undeferredFunctionType = type; + } + JavascriptMethod FunctionProxy::GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const { Assert(entryPoint->jsMethod != nullptr); @@ -2055,6 +2067,10 @@ namespace Js { func(this->deferredPrototypeType); } + if (this->undeferredFunctionType) + { + func(this->undeferredFunctionType); + } } FunctionProxy::FunctionTypeWeakRefList* FunctionProxy::EnsureFunctionObjectTypeList() @@ -4855,6 +4871,11 @@ namespace Js this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod); this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo()); } + if (this->undeferredFunctionType) + { + this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod); + this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo()); + } #if DBG if (!this->HasValidEntryPoint()) @@ -5093,6 +5114,7 @@ namespace Js // Abandon the shared type so a new function will get a new one this->deferredPrototypeType = nullptr; + this->undeferredFunctionType = nullptr; this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() | FunctionInfo::Attributes::DeferredParse)); } @@ -5153,6 +5175,11 @@ namespace Js this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod); this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo()); } + if (this->undeferredFunctionType) + { + this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod); + this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo()); + } ReinitializeExecutionModeAndLimits(); } diff --git a/lib/Runtime/Base/FunctionBody.h b/lib/Runtime/Base/FunctionBody.h index af6f508a530..d5c407f5cfd 100644 --- a/lib/Runtime/Base/FunctionBody.h +++ b/lib/Runtime/Base/FunctionBody.h @@ -1290,6 +1290,8 @@ namespace Js ProxyEntryPointInfo* GetDefaultEntryPointInfo() const; ScriptFunctionType * GetDeferredPrototypeType() const; ScriptFunctionType * EnsureDeferredPrototypeType(); + ScriptFunctionType * GetUndeferredFunctionType() const; + void SetUndeferredFunctionType(ScriptFunctionType * type); JavascriptMethod GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const; // Function object type list methods @@ -1368,6 +1370,7 @@ namespace Js FieldNoBarrier(ScriptContext*) m_scriptContext; // Memory context for this function body FieldWithBarrier(Utf8SourceInfo*) m_utf8SourceInfo; FieldWithBarrier(ScriptFunctionType*) deferredPrototypeType; + FieldWithBarrier(ScriptFunctionType*) undeferredFunctionType; FieldWithBarrier(ProxyEntryPointInfo*) m_defaultEntryPointInfo; // The default entry point info for the function proxy FieldWithBarrier(uint) m_functionNumber; // Per thread global function number diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index fe8b64eca9f..174bb784288 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -652,12 +652,13 @@ namespace Js #endif } - bool JavascriptLibrary::InitializeGeneratorFunction(DynamicObject *function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) + bool JavascriptLibrary::InitializeGeneratorFunction(DynamicObject *instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { - bool isAnonymousFunction = JavascriptGeneratorFunction::FromVar(function)->IsAnonymousFunction(); + JavascriptGeneratorFunction *function = JavascriptGeneratorFunction::FromVar(instance); + bool isAnonymousFunction = function->IsAnonymousFunction(); JavascriptLibrary* javascriptLibrary = function->GetType()->GetLibrary(); - typeHandler->Convert(function, isAnonymousFunction ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); + typeHandler->ConvertFunction(function, isAnonymousFunction ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); function->SetPropertyWithAttributes(PropertyIds::prototype, javascriptLibrary->CreateGeneratorConstructorPrototypeObject(), PropertyWritable, nullptr); if (function->GetScriptContext()->GetConfig()->IsES6FunctionNameEnabled() && !isAnonymousFunction) @@ -696,8 +697,9 @@ namespace Js } template - bool JavascriptLibrary::InitializeFunction(DynamicObject *function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) + bool JavascriptLibrary::InitializeFunction(DynamicObject *instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { + JavascriptFunction * function = JavascriptFunction::FromVar(instance); JavascriptLibrary* javascriptLibrary = function->GetType()->GetLibrary(); ScriptFunction *scriptFunction = nullptr; bool useAnonymous = false; @@ -710,19 +712,19 @@ namespace Js if (!addPrototype) { Assert(!useAnonymous); - typeHandler->Convert(function, javascriptLibrary->functionTypeHandler); + typeHandler->ConvertFunction(function, javascriptLibrary->functionTypeHandler); } else { - typeHandler->Convert(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); - function->SetProperty(PropertyIds::prototype, javascriptLibrary->CreateConstructorPrototypeObject((Js::JavascriptFunction *)function), PropertyOperation_None, nullptr); - } - - if (scriptFunction) - { - if (scriptFunction->GetFunctionInfo()->IsClassConstructor()) + typeHandler->ConvertFunction(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); + DynamicObject *protoObject = javascriptLibrary->CreateConstructorPrototypeObject(function); + if (scriptFunction && scriptFunction->GetFunctionInfo()->IsClassConstructor()) + { + function->SetPropertyWithAttributes(PropertyIds::prototype, protoObject, PropertyNone, nullptr); + } + else { - scriptFunction->SetWritable(Js::PropertyIds::prototype, FALSE); + function->SetProperty(PropertyIds::prototype, protoObject, PropertyOperation_None, nullptr); } } @@ -5168,21 +5170,6 @@ namespace Js if (ScriptFunction::Is(function)) { -#if DEBUG - if (!function->GetFunctionProxy()->GetIsAnonymousFunction()) - { - Assert(function->GetFunctionInfo()->IsConstructor() ? - (function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(this->GetScriptContext()) - || function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredPrototypeFunctionWithLengthTypeHandler(this->GetScriptContext())) - : function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredFunctionTypeHandler()); - } - else - { - Assert(function->GetFunctionInfo()->IsConstructor() ? - function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() : - function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredAnonymousFunctionTypeHandler()); - } -#endif function->ChangeType(); function->SetEntryPoint(scriptContext->CurrentCrossSiteThunk); } diff --git a/lib/Runtime/Library/ScriptFunction.cpp b/lib/Runtime/Library/ScriptFunction.cpp index ea42864b5c6..2a096780da4 100644 --- a/lib/Runtime/Library/ScriptFunction.cpp +++ b/lib/Runtime/Library/ScriptFunction.cpp @@ -271,19 +271,8 @@ namespace Js Assert(this->functionInfo->GetFunctionBody() == newFunctionInfo); Assert(!newFunctionInfo->IsDeferred()); - DynamicType * type = this->GetDynamicType(); - - // If the type is shared, it must be the shared one in the old function proxy - this->functionInfo = newFunctionInfo->GetFunctionInfo(); - if (type->GetIsShared()) - { - // the type is still shared, we can't modify it, just migrate to the shared one in the function body - this->ReplaceType(newFunctionInfo->EnsureDeferredPrototypeType()); - } - - // The type has change from the default, it is not share, just use that one. JavascriptMethod directEntryPoint = newFunctionInfo->GetDirectEntryPoint(newFunctionInfo->GetDefaultEntryPointInfo()); #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING) Assert(directEntryPoint != DefaultDeferredParsingThunk diff --git a/lib/Runtime/Types/DeferredTypeHandler.cpp b/lib/Runtime/Types/DeferredTypeHandler.cpp index 0a83e7bb7fd..ed34b27e1ca 100644 --- a/lib/Runtime/Types/DeferredTypeHandler.cpp +++ b/lib/Runtime/Types/DeferredTypeHandler.cpp @@ -8,7 +8,7 @@ namespace Js { - void DeferredTypeHandlerBase::Convert(DynamicObject * instance, DynamicTypeHandler * typeHandler) + void DeferredTypeHandlerBase::ConvertFunction(JavascriptFunction * instance, DynamicTypeHandler * typeHandler) { Assert(instance->GetDynamicType()->GetTypeHandler() == this); Assert(this->inlineSlotCapacity == typeHandler->inlineSlotCapacity); @@ -24,7 +24,27 @@ namespace Js ScriptContext* scriptContext = instance->GetScriptContext(); instance->EnsureSlots(0, typeHandler->GetSlotCapacity(), scriptContext, typeHandler); - typeHandler->SetInstanceTypeHandler(instance); + + FunctionProxy * functionProxy = instance->GetFunctionProxy(); + ScriptFunctionType * undeferredFunctionType = nullptr; + if (functionProxy) + { + undeferredFunctionType = functionProxy->GetUndeferredFunctionType(); + } + if (undeferredFunctionType) + { + Assert(undeferredFunctionType->GetIsShared()); + instance->ReplaceType(undeferredFunctionType); + } + else + { + typeHandler->SetInstanceTypeHandler(instance); + if (functionProxy && typeHandler->GetMayBecomeShared()) + { + functionProxy->SetUndeferredFunctionType(ScriptFunction::FromVar(instance)->GetScriptFunctionType()); + instance->ShareType(); + } + } // We may be changing to a type handler that already has some properties. Initialize those to undefined. const Var undefined = scriptContext->GetLibrary()->GetUndefined(); diff --git a/lib/Runtime/Types/DeferredTypeHandler.h b/lib/Runtime/Types/DeferredTypeHandler.h index 27c40fb4339..50b9b46eddb 100644 --- a/lib/Runtime/Types/DeferredTypeHandler.h +++ b/lib/Runtime/Types/DeferredTypeHandler.h @@ -21,7 +21,7 @@ namespace Js } public: - void Convert(DynamicObject * instance, DynamicTypeHandler * handler); + void ConvertFunction(JavascriptFunction * instance, DynamicTypeHandler * handler); void Convert(DynamicObject * instance, DeferredInitializeMode mode, int initSlotCapacity, BOOL hasAccessor = false); virtual void SetAllPropertiesToUndefined(DynamicObject* instance, bool invalidateFixedFields) override {}; diff --git a/lib/Runtime/Types/DynamicObject.h b/lib/Runtime/Types/DynamicObject.h index dd248b69761..d954d99971c 100644 --- a/lib/Runtime/Types/DynamicObject.h +++ b/lib/Runtime/Types/DynamicObject.h @@ -67,15 +67,9 @@ namespace Js friend class JavascriptArray; // for xplat offsetof field access 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 - friend class ModuleNamespace; // for slot setting. + friend class JavascriptOperators; + friend class JavascriptLibrary; + friend class ModuleNamespace; // for slot setting. #if ENABLE_OBJECT_SOURCE_TRACKING public: @@ -111,8 +105,6 @@ namespace Js void InitSlots(DynamicObject * instance, ScriptContext * scriptContext); void SetTypeHandler(DynamicTypeHandler * typeHandler, bool hasChanged); - void ReplaceType(DynamicType * type); - void ReplaceTypeWithPredecessorType(DynamicType * previousType); protected: DEFINE_VTABLE_CTOR(DynamicObject, RecyclableObject); @@ -138,6 +130,8 @@ namespace Js void EnsureSlots(int oldCount, int newCount, ScriptContext * scriptContext, DynamicTypeHandler * newTypeHandler = nullptr); void EnsureSlots(int newCount, ScriptContext *scriptContext); + void ReplaceType(DynamicType * type); + void ReplaceTypeWithPredecessorType(DynamicType * previousType); DynamicTypeHandler * GetTypeHandler() const;