Skip to content

Commit 2ad5e2b

Browse files
committed
Share SimpleTypeHandler types on function objects. Currently when we 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.)
1 parent 545b35e commit 2ad5e2b

File tree

7 files changed

+74
-55
lines changed

7 files changed

+74
-55
lines changed

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ namespace Js
7272
m_isTopLevel(false),
7373
m_isPublicLibraryCode(false),
7474
m_scriptContext(scriptContext),
75+
deferredPrototypeType(nullptr),
76+
undeferredFunctionType(nullptr),
7577
m_utf8SourceInfo(utf8SourceInfo),
7678
m_functionNumber(functionNumber),
7779
m_defaultEntryPointInfo(nullptr),
@@ -1472,6 +1474,8 @@ namespace Js
14721474
other->SetCachedSourceString(this->GetCachedSourceString());
14731475
CopyDeferParseField(m_isAsmjsMode);
14741476
CopyDeferParseField(m_isAsmJsFunction);
1477+
CopyDeferParseField(deferredPrototypeType);
1478+
CopyDeferParseField(undeferredFunctionType);
14751479

14761480
other->SetFunctionObjectTypeList(this->GetFunctionObjectTypeList());
14771481

@@ -2015,6 +2019,16 @@ namespace Js
20152019
return type;
20162020
}
20172021

2022+
ScriptFunctionType * FunctionProxy::GetUndeferredFunctionType() const
2023+
{
2024+
return undeferredFunctionType;
2025+
}
2026+
2027+
void FunctionProxy::SetUndeferredFunctionType(ScriptFunctionType * type)
2028+
{
2029+
undeferredFunctionType = type;
2030+
}
2031+
20182032
JavascriptMethod FunctionProxy::GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const
20192033
{
20202034
Assert(entryPoint->jsMethod != nullptr);
@@ -2055,6 +2069,8 @@ namespace Js
20552069
{
20562070
func(this->deferredPrototypeType);
20572071
}
2072+
// NOTE: We deliberately do not map the undeferredFunctionType here, since it's in the list
2073+
// of registered function object types we processed above.
20582074
}
20592075

20602076
FunctionProxy::FunctionTypeWeakRefList* FunctionProxy::EnsureFunctionObjectTypeList()
@@ -4871,6 +4887,11 @@ namespace Js
48714887
this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
48724888
this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
48734889
}
4890+
if (this->undeferredFunctionType)
4891+
{
4892+
this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
4893+
this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
4894+
}
48744895

48754896
#if DBG
48764897
if (!this->HasValidEntryPoint())
@@ -5109,6 +5130,7 @@ namespace Js
51095130

51105131
// Abandon the shared type so a new function will get a new one
51115132
this->deferredPrototypeType = nullptr;
5133+
this->undeferredFunctionType = nullptr;
51125134
this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
51135135
}
51145136

@@ -5169,6 +5191,11 @@ namespace Js
51695191
this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
51705192
this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
51715193
}
5194+
if (this->undeferredFunctionType)
5195+
{
5196+
this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
5197+
this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
5198+
}
51725199
ReinitializeExecutionModeAndLimits();
51735200
}
51745201

lib/Runtime/Base/FunctionBody.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,8 @@ namespace Js
12901290
ProxyEntryPointInfo* GetDefaultEntryPointInfo() const;
12911291
ScriptFunctionType * GetDeferredPrototypeType() const;
12921292
ScriptFunctionType * EnsureDeferredPrototypeType();
1293+
ScriptFunctionType * GetUndeferredFunctionType() const;
1294+
void SetUndeferredFunctionType(ScriptFunctionType * type);
12931295
JavascriptMethod GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const;
12941296

12951297
// Function object type list methods
@@ -1368,6 +1370,7 @@ namespace Js
13681370
FieldNoBarrier(ScriptContext*) m_scriptContext; // Memory context for this function body
13691371
FieldWithBarrier(Utf8SourceInfo*) m_utf8SourceInfo;
13701372
FieldWithBarrier(ScriptFunctionType*) deferredPrototypeType;
1373+
FieldWithBarrier(ScriptFunctionType*) undeferredFunctionType;
13711374
FieldWithBarrier(ProxyEntryPointInfo*) m_defaultEntryPointInfo; // The default entry point info for the function proxy
13721375

13731376
FieldWithBarrier(uint) m_functionNumber; // Per thread global function number

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -652,12 +652,13 @@ namespace Js
652652
#endif
653653
}
654654

655-
bool JavascriptLibrary::InitializeGeneratorFunction(DynamicObject *function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
655+
bool JavascriptLibrary::InitializeGeneratorFunction(DynamicObject *instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
656656
{
657-
bool isAnonymousFunction = JavascriptGeneratorFunction::FromVar(function)->IsAnonymousFunction();
657+
JavascriptGeneratorFunction *function = JavascriptGeneratorFunction::FromVar(instance);
658+
bool isAnonymousFunction = function->IsAnonymousFunction();
658659

659660
JavascriptLibrary* javascriptLibrary = function->GetType()->GetLibrary();
660-
typeHandler->Convert(function, isAnonymousFunction ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler);
661+
typeHandler->ConvertFunction(function, isAnonymousFunction ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler);
661662
function->SetPropertyWithAttributes(PropertyIds::prototype, javascriptLibrary->CreateGeneratorConstructorPrototypeObject(), PropertyWritable, nullptr);
662663

663664
if (function->GetScriptContext()->GetConfig()->IsES6FunctionNameEnabled() && !isAnonymousFunction)
@@ -696,8 +697,9 @@ namespace Js
696697
}
697698

698699
template<bool addPrototype>
699-
bool JavascriptLibrary::InitializeFunction(DynamicObject *function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
700+
bool JavascriptLibrary::InitializeFunction(DynamicObject *instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
700701
{
702+
JavascriptFunction * function = JavascriptFunction::FromVar(instance);
701703
JavascriptLibrary* javascriptLibrary = function->GetType()->GetLibrary();
702704
ScriptFunction *scriptFunction = nullptr;
703705
bool useAnonymous = false;
@@ -710,19 +712,19 @@ namespace Js
710712
if (!addPrototype)
711713
{
712714
Assert(!useAnonymous);
713-
typeHandler->Convert(function, javascriptLibrary->functionTypeHandler);
715+
typeHandler->ConvertFunction(function, javascriptLibrary->functionTypeHandler);
714716
}
715717
else
716718
{
717-
typeHandler->Convert(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler);
718-
function->SetProperty(PropertyIds::prototype, javascriptLibrary->CreateConstructorPrototypeObject((Js::JavascriptFunction *)function), PropertyOperation_None, nullptr);
719-
}
720-
721-
if (scriptFunction)
722-
{
723-
if (scriptFunction->GetFunctionInfo()->IsClassConstructor())
719+
typeHandler->ConvertFunction(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler);
720+
DynamicObject *protoObject = javascriptLibrary->CreateConstructorPrototypeObject(function);
721+
if (scriptFunction && scriptFunction->GetFunctionInfo()->IsClassConstructor())
722+
{
723+
function->SetPropertyWithAttributes(PropertyIds::prototype, protoObject, PropertyNone, nullptr);
724+
}
725+
else
724726
{
725-
scriptFunction->SetWritable(Js::PropertyIds::prototype, FALSE);
727+
function->SetProperty(PropertyIds::prototype, protoObject, PropertyOperation_None, nullptr);
726728
}
727729
}
728730

@@ -5168,21 +5170,6 @@ namespace Js
51685170

51695171
if (ScriptFunction::Is(function))
51705172
{
5171-
#if DEBUG
5172-
if (!function->GetFunctionProxy()->GetIsAnonymousFunction())
5173-
{
5174-
Assert(function->GetFunctionInfo()->IsConstructor() ?
5175-
(function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(this->GetScriptContext())
5176-
|| function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredPrototypeFunctionWithLengthTypeHandler(this->GetScriptContext()))
5177-
: function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredFunctionTypeHandler());
5178-
}
5179-
else
5180-
{
5181-
Assert(function->GetFunctionInfo()->IsConstructor() ?
5182-
function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() :
5183-
function->GetDynamicType()->GetTypeHandler() == JavascriptLibrary::GetDeferredAnonymousFunctionTypeHandler());
5184-
}
5185-
#endif
51865173
function->ChangeType();
51875174
function->SetEntryPoint(scriptContext->CurrentCrossSiteThunk);
51885175
}

lib/Runtime/Library/ScriptFunction.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,22 +268,10 @@ namespace Js
268268
{
269269
// Update deferred parsed/serialized function to the real function body
270270
Assert(this->functionInfo->HasBody());
271+
Assert(this->functionInfo == newFunctionInfo->GetFunctionInfo());
271272
Assert(this->functionInfo->GetFunctionBody() == newFunctionInfo);
272273
Assert(!newFunctionInfo->IsDeferred());
273274

274-
DynamicType * type = this->GetDynamicType();
275-
276-
// If the type is shared, it must be the shared one in the old function proxy
277-
278-
this->functionInfo = newFunctionInfo->GetFunctionInfo();
279-
280-
if (type->GetIsShared())
281-
{
282-
// the type is still shared, we can't modify it, just migrate to the shared one in the function body
283-
this->ReplaceType(newFunctionInfo->EnsureDeferredPrototypeType());
284-
}
285-
286-
// The type has change from the default, it is not share, just use that one.
287275
JavascriptMethod directEntryPoint = newFunctionInfo->GetDirectEntryPoint(newFunctionInfo->GetDefaultEntryPointInfo());
288276
#if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
289277
Assert(directEntryPoint != DefaultDeferredParsingThunk

lib/Runtime/Types/DeferredTypeHandler.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Js
1010
{
11-
void DeferredTypeHandlerBase::Convert(DynamicObject * instance, DynamicTypeHandler * typeHandler)
11+
void DeferredTypeHandlerBase::ConvertFunction(JavascriptFunction * instance, DynamicTypeHandler * typeHandler)
1212
{
1313
Assert(instance->GetDynamicType()->GetTypeHandler() == this);
1414
Assert(this->inlineSlotCapacity == typeHandler->inlineSlotCapacity);
@@ -24,7 +24,27 @@ namespace Js
2424

2525
ScriptContext* scriptContext = instance->GetScriptContext();
2626
instance->EnsureSlots(0, typeHandler->GetSlotCapacity(), scriptContext, typeHandler);
27-
typeHandler->SetInstanceTypeHandler(instance);
27+
28+
FunctionProxy * functionProxy = instance->GetFunctionProxy();
29+
ScriptFunctionType * undeferredFunctionType = nullptr;
30+
if (functionProxy)
31+
{
32+
undeferredFunctionType = functionProxy->GetUndeferredFunctionType();
33+
}
34+
if (undeferredFunctionType)
35+
{
36+
Assert(undeferredFunctionType->GetIsShared());
37+
instance->ReplaceType(undeferredFunctionType);
38+
}
39+
else
40+
{
41+
typeHandler->SetInstanceTypeHandler(instance);
42+
if (functionProxy && typeHandler->GetMayBecomeShared())
43+
{
44+
functionProxy->SetUndeferredFunctionType(ScriptFunction::UnsafeFromVar(instance)->GetScriptFunctionType());
45+
instance->ShareType();
46+
}
47+
}
2848

2949
// We may be changing to a type handler that already has some properties. Initialize those to undefined.
3050
const Var undefined = scriptContext->GetLibrary()->GetUndefined();

lib/Runtime/Types/DeferredTypeHandler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace Js
2121
}
2222

2323
public:
24-
void Convert(DynamicObject * instance, DynamicTypeHandler * handler);
24+
void ConvertFunction(JavascriptFunction * instance, DynamicTypeHandler * handler);
2525
void Convert(DynamicObject * instance, DeferredInitializeMode mode, int initSlotCapacity, BOOL hasAccessor = false);
2626

2727
virtual void SetAllPropertiesToUndefined(DynamicObject* instance, bool invalidateFixedFields) override {};

lib/Runtime/Types/DynamicObject.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,9 @@ namespace Js
6767

6868
friend class JavascriptArray; // for xplat offsetof field access
6969
friend class JavascriptNativeArray; // for xplat offsetof field access
70-
friend class JavascriptOperators; // for ReplaceType
71-
friend class PathTypeHandlerBase; // for ReplaceType
72-
friend class SimplePathTypeHandlerNoAttr;
73-
friend class SimplePathTypeHandlerWithAttr;
74-
friend class PathTypeHandlerNoAttr;
75-
friend class JavascriptLibrary; // for ReplaceType
76-
friend class ScriptFunction; // for ReplaceType;
77-
friend class JSON::JSONParser; //for ReplaceType
78-
friend class ModuleNamespace; // for slot setting.
70+
friend class JavascriptOperators;
71+
friend class JavascriptLibrary;
72+
friend class ModuleNamespace; // for slot setting.
7973

8074
#if ENABLE_OBJECT_SOURCE_TRACKING
8175
public:
@@ -111,8 +105,6 @@ namespace Js
111105

112106
void InitSlots(DynamicObject * instance, ScriptContext * scriptContext);
113107
void SetTypeHandler(DynamicTypeHandler * typeHandler, bool hasChanged);
114-
void ReplaceType(DynamicType * type);
115-
void ReplaceTypeWithPredecessorType(DynamicType * previousType);
116108

117109
protected:
118110
DEFINE_VTABLE_CTOR(DynamicObject, RecyclableObject);
@@ -138,6 +130,8 @@ namespace Js
138130

139131
void EnsureSlots(int oldCount, int newCount, ScriptContext * scriptContext, DynamicTypeHandler * newTypeHandler = nullptr);
140132
void EnsureSlots(int newCount, ScriptContext *scriptContext);
133+
void ReplaceType(DynamicType * type);
134+
void ReplaceTypeWithPredecessorType(DynamicType * previousType);
141135

142136
DynamicTypeHandler * GetTypeHandler() const;
143137

0 commit comments

Comments
 (0)