Skip to content

Commit

Permalink
Share SimpleTypeHandler types on function objects. Currently when we …
Browse files Browse the repository at this point in the history
…transition from DeferredTypeHandler to SimpleTypeHandler on first access to a function object's properties, we don't share the new type we create. Track an undeferred type along with the deferredPrototypeType on FunctionProxy and re-use it whenever possible for multiple instances of the same function. (This is a first step in broader sharing of function object types.)
  • Loading branch information
pleath committed Mar 1, 2018
1 parent a61618b commit 0b25d43
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 53 deletions.
27 changes: 27 additions & 0 deletions lib/Runtime/Base/FunctionBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2055,6 +2067,10 @@ namespace Js
{
func(this->deferredPrototypeType);
}
if (this->undeferredFunctionType)
{
func(this->undeferredFunctionType);
}
}

FunctionProxy::FunctionTypeWeakRefList* FunctionProxy::EnsureFunctionObjectTypeList()
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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();
}

Expand Down
3 changes: 3 additions & 0 deletions lib/Runtime/Base/FunctionBody.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
43 changes: 15 additions & 28 deletions lib/Runtime/Library/JavascriptLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -696,8 +697,9 @@ namespace Js
}

template<bool addPrototype>
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;
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
Expand Down
11 changes: 0 additions & 11 deletions lib/Runtime/Library/ScriptFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 22 additions & 2 deletions lib/Runtime/Types/DeferredTypeHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Types/DeferredTypeHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
Expand Down
16 changes: 5 additions & 11 deletions lib/Runtime/Types/DynamicObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand Down

0 comments on commit 0b25d43

Please sign in to comment.