Skip to content

Commit 9d87ba3

Browse files
committed
Fixes asserts in Speedometer
- Some asserts in inline caches confused Null and Missing. CEWOs may produce the latter. - Missing property results should not be cached by inline caches if comes from CEWO when implicit calls are disabled - that may change without type changes. - rationalizing the marshaling story in CEWO. - The idea is to _not_ marshal on Get and the like, but on Set/Define marshal to the script contect of the containing CEWO. The latter scenario arises when initialization is deferred and needs to be done in a context different from when the object was created. We just want to maintain that object and its property data gave the same context. Otherwise we have inconsistencies in what comes from cached direct accesses and actual property accesses (causes asserts, at least, perhaps worse).
1 parent 1566986 commit 9d87ba3

File tree

4 files changed

+44
-13
lines changed

4 files changed

+44
-13
lines changed

lib/Runtime/Language/InlineCache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,8 @@ namespace Js
461461
DebugOnly(Var getPropertyValue = JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext));
462462
Assert(*propertyValue == getPropertyValue ||
463463
(VarIs<RootObjectBase>(propertyObject) && *propertyValue == JavascriptOperators::GetRootProperty(propertyObject, propertyId, requestContext))||
464-
// In some cases, such as CustomExternalObject, if implicit calls are disabled GetPropertyQuery may return null. See CustomExternalObject::GetPropertyQuery for an example.
465-
(getPropertyValue == requestContext->GetLibrary()->GetNull() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsExternal()));
464+
// In some cases, such as CustomExternalObject, if implicit calls are disabled GetPropertyQuery may return missing. See CustomExternalWrapperObject::GetPropertyQuery for an example.
465+
(getPropertyValue == requestContext->GetMissingPropertyResult() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsJsrtExternal()));
466466
}
467467
};
468468
};

lib/Runtime/Language/JavascriptOperators.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,13 +1883,19 @@ using namespace Js;
18831883
void JavascriptOperators::TryCacheMissingProperty(Var instance, Var cacheInstance, bool isRoot, PropertyId propertyId, ScriptContext* requestContext, _Inout_ PropertyValueInfo * info)
18841884
{
18851885
// Here, any well-behaved subclasses of DynamicObject can opt in to getting included in the missing property cache.
1886-
// For now, we only include basic objects and arrays. CustomExternalObject in particular is problematic because in
1887-
// some cases it can add new properties without transitioning its type handler.
1886+
// For now, we only include basic objects and arrays.
18881887
if (PHASE_OFF1(MissingPropertyCachePhase) || isRoot || !(DynamicObject::IsBaseDynamicObject(instance) || DynamicObject::IsAnyArray(instance)))
18891888
{
18901889
return;
18911890
}
18921891

1892+
// CustomExternalObject in particular is problematic because in some cases it can report missing when implicit callsare disabled.
1893+
// See CustomExternalObject::GetPropertyQuery for an example.
1894+
if (UnsafeVarTo<DynamicObject>(instance)->GetType()->IsJsrtExternal() && requestContext->GetThreadContext()->IsDisableImplicitCall())
1895+
{
1896+
return;
1897+
}
1898+
18931899
DynamicTypeHandler* handler = UnsafeVarTo<DynamicObject>(instance)->GetDynamicType()->GetTypeHandler();
18941900

18951901
// Only cache missing property lookups for non-root field loads on objects that have PathTypeHandlers, because only these types have the right behavior

lib/Runtime/Library/CustomExternalWrapperObject.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,14 +308,17 @@ void CustomExternalWrapperObject::SetPrototype(RecyclableObject* newPrototype)
308308
return;
309309
}
310310

311+
// setting the value could be deferred and now we are on a different context from
312+
// make sure the value has the same context as the containing object.
313+
newPrototype = VarTo<RecyclableObject>(Js::CrossSite::MarshalVar(this->GetScriptContext(), newPrototype));
311314
DynamicObject::SetPrototype(newPrototype);
312315
}
313316

314317
Js::Var CustomExternalWrapperObject::GetValueFromDescriptor(Js::Var instance, Js::PropertyDescriptor propertyDescriptor, Js::ScriptContext * requestContext)
315318
{
316319
if (propertyDescriptor.ValueSpecified())
317320
{
318-
return Js::CrossSite::MarshalVar(requestContext, propertyDescriptor.GetValue());
321+
return propertyDescriptor.GetValue();
319322
}
320323
if (propertyDescriptor.GetterSpecified())
321324
{
@@ -588,11 +591,20 @@ BOOL CustomExternalWrapperObject::DefineOwnPropertyDescriptor(Js::RecyclableObje
588591
Assert(!requestContext->IsHeapEnumInProgress());
589592
if (nullptr == defineOwnPropertyMethod)
590593
{
594+
Js::PropertyDescriptor desc = descriptor;
595+
596+
if(desc.ValueSpecified())
597+
{
598+
// setting the value could be deferred and now we are on a different context from
599+
// make sure the value has the same context as the containing object.
600+
desc.SetValue(Js::CrossSite::MarshalVar(targetObj->GetScriptContext(), descriptor.GetValue()));
601+
}
602+
591603
PropertyDescriptor currentDescriptor;
592604
BOOL isCurrentDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(obj, propId, requestContext, &currentDescriptor);
593605

594606
bool isExtensible = !!obj->IsExtensible();
595-
return Js::JavascriptOperators::ValidateAndApplyPropertyDescriptor<true>(obj, propId, descriptor, isCurrentDescriptorDefined ? &currentDescriptor : nullptr, isExtensible, throwOnError, requestContext);
607+
return Js::JavascriptOperators::ValidateAndApplyPropertyDescriptor<true>(obj, propId, desc, isCurrentDescriptorDefined ? &currentDescriptor : nullptr, isExtensible, throwOnError, requestContext);
596608
}
597609

598610
Js::Var descVar = descriptor.GetOriginal();
@@ -651,6 +663,9 @@ BOOL CustomExternalWrapperObject::SetPropertyTrap(Js::Var receiver, SetPropertyT
651663
};
652664
auto fn = [&]()->BOOL
653665
{
666+
// setting the value could be deferred and now we are on a different context from
667+
// make sure the value has the same context as the containing object.
668+
newValue = Js::CrossSite::MarshalVar(this->GetScriptContext(), newValue);
654669
return DynamicObject::SetProperty(propertyNameString, newValue, propertyOperationFlags, nullptr);
655670
};
656671
return SetPropertyTrap(receiver, setPropertyTrapKind, getPropertyName, newValue, requestContext, propertyOperationFlags, FALSE, fn);
@@ -708,7 +723,6 @@ BOOL CustomExternalWrapperObject::SetPropertyTrap(Js::Var receiver, SetPropertyT
708723
Js::PropertyQueryFlags CustomExternalWrapperObject::GetPropertyQuery(Js::Var originalInstance, Js::PropertyId propertyId, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext)
709724
{
710725
if (!this->VerifyObjectAlive()) return Js::PropertyQueryFlags::Property_NotFound;
711-
originalInstance = Js::CrossSite::MarshalVar(GetScriptContext(), originalInstance);
712726
Js::PropertyDescriptor result;
713727

714728
auto fn = [&](Js::RecyclableObject* object)-> BOOL {
@@ -735,7 +749,6 @@ Js::PropertyQueryFlags CustomExternalWrapperObject::GetPropertyQuery(Js::Var ori
735749
Js::PropertyQueryFlags CustomExternalWrapperObject::GetPropertyQuery(Js::Var originalInstance, Js::JavascriptString* propertyNameString, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext)
736750
{
737751
if (!this->VerifyObjectAlive()) return Js::PropertyQueryFlags::Property_NotFound;
738-
originalInstance = Js::CrossSite::MarshalVar(GetScriptContext(), originalInstance);
739752
Js::PropertyDescriptor result;
740753

741754
auto fn = [&](Js::RecyclableObject* object)-> BOOL {
@@ -766,7 +779,6 @@ Js::PropertyQueryFlags CustomExternalWrapperObject::GetPropertyQuery(Js::Var ori
766779
Js::PropertyQueryFlags CustomExternalWrapperObject::GetPropertyReferenceQuery(Js::Var originalInstance, Js::PropertyId propertyId, Js::Var* value, Js::PropertyValueInfo* info, Js::ScriptContext* requestContext)
767780
{
768781
if (!this->VerifyObjectAlive()) return Js::PropertyQueryFlags::Property_NotFound;
769-
originalInstance = Js::CrossSite::MarshalVar(GetScriptContext(), originalInstance);
770782
Js::PropertyDescriptor result;
771783

772784
auto fn = [&](Js::RecyclableObject* object)-> BOOL {
@@ -826,7 +838,6 @@ BOOL CustomExternalWrapperObject::HasOwnItem(uint32 index)
826838
Js::PropertyQueryFlags CustomExternalWrapperObject::GetItemQuery(Js::Var originalInstance, uint32 index, Js::Var* value, Js::ScriptContext * requestContext)
827839
{
828840
if (!this->VerifyObjectAlive()) return Js::PropertyQueryFlags::Property_NotFound;
829-
originalInstance = Js::CrossSite::MarshalVar(GetScriptContext(), originalInstance);
830841
Js::PropertyDescriptor result;
831842

832843
auto fn = [&](Js::RecyclableObject* object)-> BOOL {
@@ -857,7 +868,6 @@ Js::PropertyQueryFlags CustomExternalWrapperObject::GetItemQuery(Js::Var origina
857868
Js::PropertyQueryFlags CustomExternalWrapperObject::GetItemReferenceQuery(Js::Var originalInstance, uint32 index, Js::Var* value, Js::ScriptContext * requestContext)
858869
{
859870
if (!this->VerifyObjectAlive()) return Js::PropertyQueryFlags::Property_NotFound;
860-
originalInstance = Js::CrossSite::MarshalVar(GetScriptContext(), originalInstance);
861871

862872
Js::PropertyDescriptor result;
863873
auto fn = [&](Js::RecyclableObject* object)-> BOOL {
@@ -898,11 +908,17 @@ BOOL CustomExternalWrapperObject::SetItem(uint32 index, Js::Var value, Js::Prope
898908

899909
auto fn = [&]()->BOOL
900910
{
911+
// setting the value could be deferred and now we are on a different context from
912+
// make sure the value has the same context as the containing object.
913+
value = Js::CrossSite::MarshalVar(this->GetScriptContext(), value);
901914
return DynamicObject::SetItem(index, value, flags);
902915
};
903916
BOOL trapResult = SetPropertyTrap(this, CustomExternalWrapperObject::SetPropertyTrapKind::SetItemKind, getPropertyName, value, GetScriptContext(), flags, FALSE, fn);
904917
if (!trapResult)
905918
{
919+
// setting the value could be deferred and now we are on a different context from
920+
// make sure the value has the same context as the containing object.
921+
value = Js::CrossSite::MarshalVar(this->GetScriptContext(), value);
906922
return Js::DynamicObject::SetItem(index, value, flags);
907923
}
908924

@@ -1020,6 +1036,7 @@ BOOL CustomExternalWrapperObject::GetEnumerator(Js::JavascriptStaticEnumerator *
10201036
// if (desc.enumerable) yield key;
10211037
if (desc.IsEnumerable())
10221038
{
1039+
//TODO: (vsadov) not sure if should marshal here, it is "getting"
10231040
return VarTo<JavascriptString>(CrossSite::MarshalVar(
10241041
scriptContext, propertyName, propertyName->GetScriptContext()));
10251042
}
@@ -1040,7 +1057,6 @@ BOOL CustomExternalWrapperObject::GetEnumerator(Js::JavascriptStaticEnumerator *
10401057
BOOL CustomExternalWrapperObject::SetProperty(Js::PropertyId propertyId, Js::Var value, Js::PropertyOperationFlags flags, Js::PropertyValueInfo* info)
10411058
{
10421059
if (!this->VerifyObjectAlive()) return FALSE;
1043-
//value = Js::CrossSite::MarshalVar(GetScriptContext(), value);
10441060
PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
10451061

10461062
auto getPropertyName = [&](Js::ScriptContext * requestContext, Js::Var * isPropertyNameNumeric, Js::Var * propertyNameNumericValue)->Js::Var
@@ -1050,6 +1066,9 @@ BOOL CustomExternalWrapperObject::SetProperty(Js::PropertyId propertyId, Js::Var
10501066

10511067
auto fn = [&]()->BOOL
10521068
{
1069+
// setting the value could be deferred and now we are on a different context from
1070+
// make sure the value has the same context as the containing object.
1071+
value = Js::CrossSite::MarshalVar(this->GetScriptContext(), value);
10531072
return DynamicObject::SetProperty(propertyId, value, flags, info);
10541073
};
10551074

@@ -1068,6 +1087,9 @@ BOOL CustomExternalWrapperObject::SetProperty(Js::JavascriptString* propertyName
10681087

10691088
auto fn = [&]()->BOOL
10701089
{
1090+
// setting the value could be deferred and now we are on a different context from
1091+
// make sure the value has the same context as the containing object.
1092+
value = Js::CrossSite::MarshalVar(this->GetScriptContext(), value);
10711093
return DynamicObject::SetProperty(propertyNameString, value, flags, info);
10721094
};
10731095

@@ -1101,6 +1123,9 @@ BOOL CustomExternalWrapperObject::InitProperty(PropertyId propertyId, Var value,
11011123
return FALSE;
11021124
}
11031125

1126+
// setting the value could be deferred and now we are on a different context from
1127+
// make sure the value has the same context as the containing object.
1128+
value = Js::CrossSite::MarshalVar(this->GetScriptContext(), value);
11041129
return DynamicObject::InitProperty(propertyId, value, flags, info);
11051130
}
11061131

lib/Runtime/Types/TypePropertyCache.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ namespace Js
302302
DebugOnly(Var getPropertyValue = JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext));
303303
Assert(*propertyValue == getPropertyValue ||
304304
// In some cases, such as CustomExternalObject, if implicit calls are disabled GetPropertyQuery may return null. See CustomExternalObject::GetPropertyQuery for an example.
305-
(getPropertyValue == requestContext->GetLibrary()->GetNull() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsExternal()));
305+
(getPropertyValue == requestContext->GetMissingPropertyResult() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsJsrtExternal()));
306306
}
307307

308308
if(propertyObject->GetScriptContext() != requestContext)

0 commit comments

Comments
 (0)