forked from chakra-core/ChakraCore
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConstructorCache.cpp
More file actions
219 lines (198 loc) · 10.1 KB
/
Copy pathConstructorCache.cpp
File metadata and controls
219 lines (198 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
namespace Js
{
ConstructorCache ConstructorCache::DefaultInstance;
ConstructorCache::ConstructorCache() : PropertyGuard()
{
this->Invalidate();
this->content.scriptContext = nullptr;
this->content.slotCount = 0;
this->content.inlineSlotCount = 0;
this->content.updateAfterCtor = false;
this->content.ctorHasNoExplicitReturnValue = false;
this->content.skipDefaultNewObject = false;
this->content.isPopulated = false;
this->content.isPolymorphic = false;
this->content.typeUpdatePending = false;
this->content.typeIsFinal = false;
this->content.hasPrototypeChanged = false;
this->content.callCount = 0;
Assert(IsConsistent());
}
ConstructorCache::ConstructorCache(ConstructorCache const * other) : PropertyGuard(other->GetValue())
{
Assert(other != nullptr);
this->content.scriptContext = other->content.scriptContext;
this->content.slotCount = other->content.slotCount;
this->content.inlineSlotCount = other->content.inlineSlotCount;
this->content.updateAfterCtor = other->content.updateAfterCtor;
this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
this->content.isPopulated = other->content.isPopulated;
this->content.isPolymorphic = other->content.isPolymorphic;
this->content.typeUpdatePending = other->content.typeUpdatePending;
this->content.typeIsFinal = other->content.typeIsFinal;
this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
this->content.callCount = other->content.callCount;
Assert(IsConsistent());
}
void ConstructorCache::Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
{
Assert(scriptContext == type->GetScriptContext());
Assert(type->GetIsShared());
Assert(IsConsistent());
Assert(!this->content.isPopulated || this->content.isPolymorphic);
Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
this->content.isPopulated = true;
this->SetValue((intptr_t)type);
this->content.scriptContext = scriptContext;
this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
this->content.updateAfterCtor = updateAfterCtor;
Assert(IsConsistent());
}
void ConstructorCache::PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
{
Assert(IsConsistent());
Assert(!this->content.isPopulated);
this->content.isPopulated = true;
this->SetValue((intptr_t)CtorCacheGuardValues::Special);
this->content.scriptContext = scriptContext;
this->content.skipDefaultNewObject = true;
Assert(IsConsistent());
}
bool ConstructorCache::TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
{
Assert(scriptContext == type->GetScriptContext());
Assert(type->GetTypeHandler()->GetMayBecomeShared());
Assert(IsConsistent());
Assert(this->content.isPopulated);
Assert(this->content.scriptContext == scriptContext);
Assert(!this->content.typeUpdatePending);
Assert(this->content.ctorHasNoExplicitReturnValue);
if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
{
return false;
}
if (type->GetIsShared())
{
this->SetValue((intptr_t)type);
this->content.typeIsFinal = true;
this->content.pendingType = nullptr;
}
else
{
AssertMsg(false, "No one calls this part of the code?");
this->SetValue((intptr_t)CtorCacheGuardValues::Special);
this->content.pendingType = type;
this->content.typeUpdatePending = true;
}
this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
Assert(IsConsistent());
return true;
}
void ConstructorCache::UpdateInlineSlotCount()
{
Assert(IsConsistent());
Assert(this->content.isPopulated);
Assert(IsEnabled() || NeedsTypeUpdate());
DynamicType* type;
if (this->content.typeUpdatePending)
{
type = this->content.pendingType;
}
else
{
type = reinterpret_cast<DynamicType*>(this->GetValue());
}
DynamicTypeHandler* typeHandler = type->GetTypeHandler();
// Inline slot capacity should never grow as a result of shrinking.
Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
// Slot capacity should never grow as a result of shrinking.
Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
this->content.slotCount = typeHandler->GetSlotCapacity();
this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
Assert(IsConsistent());
}
void ConstructorCache::EnableAfterTypeUpdate()
{
Assert(IsConsistent());
Assert(this->content.isPopulated);
Assert(!IsEnabled());
Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special);
Assert(this->content.typeUpdatePending);
Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
Assert(this->content.pendingType->GetIsShared());
DynamicType* pendingType = this->content.pendingType;
this->SetValue((intptr_t)pendingType);
this->content.typeIsFinal = true;
this->content.pendingType = nullptr;
this->content.typeUpdatePending = false;
Assert(IsConsistent());
}
ConstructorCache* ConstructorCache::EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext)
{
Assert(currentCache != nullptr);
ConstructorCache* newCache = currentCache;
// If the old cache has been invalidated, we need to create a new one to avoid incorrectly re-validating
// caches that may have been hard-coded in the JIT-ed code with different prototype and type. However, if
// the cache is already polymorphic, it will not be hard-coded, and hence we don't need to allocate a new
// one - in case the prototype property changes frequently.
if (ConstructorCache::IsDefault(currentCache) || (currentCache->IsInvalidated() && !currentCache->IsPolymorphic()))
{
// Review (jedmiad): I don't think we need to zero the struct, since we initialize each field.
newCache = RecyclerNew(scriptContext->GetRecycler(), ConstructorCache);
// TODO: Consider marking the cache as polymorphic only if the prototype and type actually changed. In fact,
// if they didn't change we could reuse the same cache and simply mark it as valid. Not really true. The cache
// might have been invalidated due to a property becoming read-only. In that case we can't re-validate an old
// monomorphic cache. We must allocate a new one.
newCache->content.isPolymorphic = currentCache->content.isPopulated && currentCache->content.hasPrototypeChanged;
}
// If we kept the old invalidated cache, it better be marked as polymorphic.
Assert(!newCache->IsInvalidated() || newCache->IsPolymorphic());
// If the cache was polymorphic, we shouldn't have allocated a new one.
Assert(!currentCache->IsPolymorphic() || newCache == currentCache);
return newCache;
}
void ConstructorCache::InvalidateOnPrototypeChange()
{
if (IsDefault(this))
{
Assert((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Invalid);
Assert(!this->content.isPopulated);
}
else if ((CtorCacheGuardValues)this->GetValue() == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject)
{
// Do nothing. If we skip the default object, changes to the prototype property don't affect
// what we'll do during object allocation.
// Can't assert the following because we set the prototype property during library initialization.
// AssertMsg(false, "Overriding a prototype on a built-in constructor should be illegal.");
}
else
{
this->Invalidate();
this->content.hasPrototypeChanged = true;
// Make sure we don't leak the old type.
this->content.pendingType = nullptr;
Assert(this->content.pendingType == nullptr);
Assert(IsInvalidated());
}
Assert(IsConsistent());
}
#if DBG_DUMP
void ConstructorCache::Dump() const
{
Output::Print(_u("guard value or type = 0x%p, script context = 0x%p, pending type = 0x%p, slots = %d, inline slots = %d, populated = %d, polymorphic = %d, update cache = %d, update type = %d, skip default = %d, no return = %d"),
this->GetRawGuardValue(), this->GetScriptContext(), this->GetPendingType(), this->GetSlotCount(), this->GetInlineSlotCount(),
this->IsPopulated(), this->IsPolymorphic(), this->GetUpdateCacheAfterCtor(), this->GetTypeUpdatePending(),
this->GetSkipDefaultNewObject(), this->GetCtorHasNoExplicitReturnValue());
}
#endif
}