blob: 25b64b9f6333dccdeebea72bca9b98555536f539 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeDebugPch.h"
#if ENABLE_TTD
#include "ByteCode/ByteCodeSerializer.h"
namespace TTD
{
namespace JsSupport
{
bool IsVarTaggedInline(Js::Var v)
{
return Js::TaggedNumber::Is(v);
}
bool IsVarPtrValued(Js::Var v)
{
return !Js::TaggedNumber::Is(v);
}
bool IsVarPrimitiveKind(Js::Var v)
{
if(Js::TaggedNumber::Is(v))
{
return false;
}
Js::TypeId tid = Js::RecyclableObject::FromVar(v)->GetTypeId();
return tid <= Js::TypeIds_LastToPrimitiveType;
}
bool IsVarComplexKind(Js::Var v)
{
if(Js::TaggedNumber::Is(v))
{
return false;
}
Js::TypeId tid = Js::RecyclableObject::FromVar(v)->GetTypeId();
return tid > Js::TypeIds_LastToPrimitiveType;
}
#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
bool AreInlineVarsEquiv(Js::Var v1, Js::Var v2)
{
if(v1 == v2)
{
return true; //same bit pattern so no problem
}
if(v1 == nullptr || v2 == nullptr)
{
return false; //then they should be the same per above
}
if(Js::TaggedNumber::Is(v1) != Js::TaggedNumber::Is(v2))
{
return false;
}
double v1val = Js::TaggedInt::Is(v1) ? Js::TaggedInt::ToInt32(v1) : Js::JavascriptNumber::GetValue(v1);
double v2val = Js::TaggedInt::Is(v2) ? Js::TaggedInt::ToInt32(v2) : Js::JavascriptNumber::GetValue(v2);
if(Js::JavascriptNumber::IsNan(v1val) != Js::JavascriptNumber::IsNan(v2val))
{
return false;
}
return v1val == v2val;
}
#endif
Js::FunctionBody* ForceAndGetFunctionBody(Js::ParseableFunctionInfo* pfi)
{
Js::FunctionBody* fb = nullptr;
if(pfi->IsDeferredDeserializeFunction())
{
Js::DeferDeserializeFunctionInfo* deferDeserializeInfo = pfi->GetDeferDeserializeFunctionInfo();
fb = deferDeserializeInfo->Deserialize();
}
else
{
if(pfi->IsDeferredParseFunction())
{
fb = pfi->GetParseableFunctionInfo()->Parse();
}
else
{
fb = pfi->GetFunctionBody();
}
}
TTDAssert(fb != nullptr, "I just want a function body!!!");
fb->EnsureDeserialized();
return fb;
}
void WriteCodeToFile(ThreadContext* threadContext, bool fromEvent, uint32 bodyId, bool isUtf8Source, byte* sourceBuffer, uint32 length)
{
char asciiResourceName[64];
sprintf_s(asciiResourceName, 64, "src%s_%I32u.js", (fromEvent ? "_ld" : ""), bodyId);
TTDataIOInfo& iofp = threadContext->TTDContext->TTDataIOInfo;
JsTTDStreamHandle srcStream = iofp.pfOpenResourceStream(iofp.ActiveTTUriLength, iofp.ActiveTTUri, strlen(asciiResourceName), asciiResourceName, false, true);
TTDAssert(srcStream != nullptr, "Failed to open code resource stream for writing.");
if(isUtf8Source)
{
byte byteOrderArray[3] = { 0xEF, 0xBB, 0xBF };
size_t byteOrderCount = 0;
bool okBOC = iofp.pfWriteBytesToStream(srcStream, byteOrderArray, _countof(byteOrderArray), &byteOrderCount);
TTDAssert(okBOC && byteOrderCount == _countof(byteOrderArray), "Write Failed!!!");
}
else
{
byte byteOrderArray[2] = { 0xFF, 0xFE };
size_t byteOrderCount = 0;
bool okBOC = iofp.pfWriteBytesToStream(srcStream, byteOrderArray, _countof(byteOrderArray), &byteOrderCount);
TTDAssert(okBOC && byteOrderCount == _countof(byteOrderArray), "Write Failed!!!");
}
size_t writtenCount = 0;
bool ok = iofp.pfWriteBytesToStream(srcStream, sourceBuffer, length, &writtenCount);
TTDAssert(ok && writtenCount == length, "Write Failed!!!");
iofp.pfFlushAndCloseStream(srcStream, false, true);
}
void ReadCodeFromFile(ThreadContext* threadContext, bool fromEvent, uint32 bodyId, bool isUtf8Source, byte* sourceBuffer, uint32 length)
{
char asciiResourceName[64];
sprintf_s(asciiResourceName, 64, "src%s_%I32u.js", (fromEvent ? "_ld" : ""), bodyId);
TTDataIOInfo& iofp = threadContext->TTDContext->TTDataIOInfo;
JsTTDStreamHandle srcStream = iofp.pfOpenResourceStream(iofp.ActiveTTUriLength, iofp.ActiveTTUri, strlen(asciiResourceName), asciiResourceName, true, false);
TTDAssert(srcStream != nullptr, "Failed to open code resource stream for reading.");
if(isUtf8Source)
{
byte byteOrderArray[3] = { 0x0, 0x0, 0x0 };
size_t byteOrderCount = 0;
bool okBOC = iofp.pfReadBytesFromStream(srcStream, byteOrderArray, _countof(byteOrderArray), &byteOrderCount);
TTDAssert(okBOC && byteOrderCount == _countof(byteOrderArray) && byteOrderArray[0] == 0xEF && byteOrderArray[1] == 0xBB && byteOrderArray[2] == 0xBF, "Read Failed!!!");
}
else
{
byte byteOrderArray[2] = { 0x0, 0x0 };
size_t byteOrderCount = 0;
bool okBOC = iofp.pfReadBytesFromStream(srcStream, byteOrderArray, _countof(byteOrderArray), &byteOrderCount);
TTDAssert(okBOC && byteOrderCount == _countof(byteOrderArray) && byteOrderArray[0] == 0xFF && byteOrderArray[1] == 0xFE, "Read Failed!!!");
}
size_t readCount = 0;
bool ok = iofp.pfReadBytesFromStream(srcStream, sourceBuffer, length, &readCount);
TTDAssert(ok && readCount == length, "Read Failed!!!");
iofp.pfFlushAndCloseStream(srcStream, true, false);
}
}
namespace NSSnapValues
{
void EmitTTDVar(TTDVar var, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
if(var == nullptr)
{
writer->WriteTag<TTDVarEmitTag>(NSTokens::Key::ttdVarTag, TTDVarEmitTag::TTDVarNull);
writer->WriteNull(NSTokens::Key::nullVal, NSTokens::Separator::CommaSeparator);
}
else if(Js::TaggedNumber::Is(var))
{
#if FLOATVAR
if(Js::TaggedInt::Is(var))
{
#endif
writer->WriteTag<TTDVarEmitTag>(NSTokens::Key::ttdVarTag, TTDVarEmitTag::TTDVarInt);
writer->WriteInt32(NSTokens::Key::i32Val, Js::TaggedInt::ToInt32(var), NSTokens::Separator::CommaSeparator);
#if FLOATVAR
}
else
{
TTDAssert(Js::JavascriptNumber::Is_NoTaggedIntCheck(var), "Only other tagged value we support!!!");
writer->WriteTag<TTDVarEmitTag>(NSTokens::Key::ttdVarTag, TTDVarEmitTag::TTDVarDouble);
writer->WriteDouble(NSTokens::Key::doubleVal, Js::JavascriptNumber::GetValue(var), NSTokens::Separator::CommaSeparator);
}
#endif
}
else
{
writer->WriteTag<TTDVarEmitTag>(NSTokens::Key::ttdVarTag, TTDVarEmitTag::TTDVarAddr);
writer->WriteAddr(NSTokens::Key::ptrIdVal, TTD_CONVERT_VAR_TO_PTR_ID(var), NSTokens::Separator::CommaSeparator);
}
writer->WriteRecordEnd();
}
TTDVar ParseTTDVar(bool readSeperator, FileReader* reader)
{
reader->ReadRecordStart(readSeperator);
TTDVar res = nullptr;
TTDVarEmitTag tag = reader->ReadTag<TTDVarEmitTag>(NSTokens::Key::ttdVarTag);
if(tag == TTDVarEmitTag::TTDVarNull)
{
reader->ReadNull(NSTokens::Key::nullVal, true);
res = nullptr;
}
else if(tag == TTDVarEmitTag::TTDVarInt)
{
int32 intVal = reader->ReadInt32(NSTokens::Key::i32Val, true);
res = Js::TaggedInt::ToVarUnchecked(intVal);
}
#if FLOATVAR
else if(tag == TTDVarEmitTag::TTDVarDouble)
{
double doubleVal = reader->ReadDouble(NSTokens::Key::doubleVal, true);
res = Js::JavascriptNumber::NewInlined(doubleVal, nullptr);
}
#endif
else
{
TTDAssert(tag == TTDVarEmitTag::TTDVarAddr, "Is there something else?");
TTD_PTR_ID addrVal = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);
res = TTD_COERCE_PTR_ID_TO_VAR(addrVal);
}
reader->ReadRecordEnd();
return res;
}
#if ENABLE_SNAPSHOT_COMPARE
bool CheckSnapEquivTTDDouble(double d1, double d2)
{
if(Js::JavascriptNumber::IsNan(d1) || Js::JavascriptNumber::IsNan(d2))
{
return (Js::JavascriptNumber::IsNan(d1) && Js::JavascriptNumber::IsNan(d2));
}
else
{
return (d1 == d2);
}
}
void AssertSnapEquivTTDVar_Helper(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, TTDComparePath::StepKind stepKind, const TTDComparePath::PathEntry& next)
{
if(v1 == nullptr || v2 == nullptr)
{
compareMap.DiagnosticAssert(v1 == v2);
}
else if(Js::TaggedNumber::Is(v1) || Js::TaggedNumber::Is(v2))
{
compareMap.DiagnosticAssert(JsSupport::AreInlineVarsEquiv(v1, v2));
}
else
{
compareMap.CheckConsistentAndAddPtrIdMapping_Helper(TTD_CONVERT_VAR_TO_PTR_ID(v1), TTD_CONVERT_VAR_TO_PTR_ID(v2), stepKind, next);
}
}
void AssertSnapEquivTTDVar_Property(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid)
{
TTDComparePath::PathEntry next{ (uint32)pid, nullptr };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::PropertyData, next);
}
void AssertSnapEquivTTDVar_PropertyGetter(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid)
{
TTDComparePath::PathEntry next{ (uint32)pid, nullptr };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::PropertyGetter, next);
}
void AssertSnapEquivTTDVar_PropertySetter(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid)
{
TTDComparePath::PathEntry next{ (uint32)pid, nullptr };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::PropertySetter, next);
}
void AssertSnapEquivTTDVar_Array(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, uint32 index)
{
TTDComparePath::PathEntry next{ index, nullptr };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::Array, next);
}
void AssertSnapEquivTTDVar_SlotArray(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, uint32 index)
{
TTDComparePath::PathEntry next{ index, nullptr };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::SlotArray, next);
}
void AssertSnapEquivTTDVar_Special(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, const char16* specialField)
{
TTDComparePath::PathEntry next{ -1, specialField };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::Special, next);
}
void AssertSnapEquivTTDVar_SpecialArray(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, const char16* specialField, uint32 index)
{
TTDComparePath::PathEntry next{ index, specialField };
AssertSnapEquivTTDVar_Helper(v1, v2, compareMap, TTDComparePath::StepKind::SpecialArray, next);
}
#endif
//////////////////
void ExtractSnapPrimitiveValue(SnapPrimitiveValue* snapValue, Js::RecyclableObject* jsValue, bool isWellKnown, const TTDIdentifierDictionary<TTD_PTR_ID, NSSnapType::SnapType*>& idToTypeMap, SlabAllocator& alloc)
{
snapValue->PrimitiveValueId = TTD_CONVERT_VAR_TO_PTR_ID(jsValue);
Js::Type* objType = jsValue->GetType();
snapValue->SnapType = idToTypeMap.LookupKnownItem(TTD_CONVERT_TYPEINFO_TO_PTR_ID(objType));
TTD_WELLKNOWN_TOKEN lookupToken = isWellKnown ? jsValue->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownObject(jsValue) : TTD_INVALID_WELLKNOWN_TOKEN;
snapValue->OptWellKnownToken = alloc.CopyRawNullTerminatedStringInto(lookupToken);
if(snapValue->OptWellKnownToken == TTD_INVALID_WELLKNOWN_TOKEN)
{
Js::JavascriptLibrary* jslib = jsValue->GetLibrary();
switch(snapValue->SnapType->JsTypeId)
{
case Js::TypeIds_Undefined:
case Js::TypeIds_Null:
break;
case Js::TypeIds_Boolean:
snapValue->u_boolValue = Js::JavascriptBoolean::FromVar(jsValue)->GetValue();
break;
case Js::TypeIds_Number:
snapValue->u_doubleValue = Js::JavascriptNumber::GetValue(jsValue);
break;
case Js::TypeIds_Int64Number:
snapValue->u_int64Value = Js::JavascriptInt64Number::FromVar(jsValue)->GetValue();
break;
case Js::TypeIds_UInt64Number:
snapValue->u_uint64Value = Js::JavascriptUInt64Number::FromVar(jsValue)->GetValue();
break;
case Js::TypeIds_String:
snapValue->u_stringValue = alloc.SlabAllocateStruct<TTString>();
alloc.CopyStringIntoWLength(Js::JavascriptString::FromVar(jsValue)->GetSz(), Js::JavascriptString::FromVar(jsValue)->GetLength(), *(snapValue->u_stringValue));
break;
case Js::TypeIds_Symbol:
snapValue->u_propertyIdValue = jslib->ExtractPrimitveSymbolId_TTD(jsValue);
break;
default:
TTDAssert(false, "These are supposed to be primitive values on the heap e.g., no pointers or properties.");
break;
}
}
}
void InflateSnapPrimitiveValue(const SnapPrimitiveValue* snapValue, InflateMap* inflator)
{
Js::ScriptContext* ctx = inflator->LookupScriptContext(snapValue->SnapType->ScriptContextLogId);
Js::JavascriptLibrary* jslib = ctx->GetLibrary();
Js::Var res = nullptr;
if(snapValue->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN)
{
res = ctx->TTDWellKnownInfo->LookupKnownObjectFromPath(snapValue->OptWellKnownToken);
}
else
{
Js::RecyclableObject* rObj = inflator->FindReusableObjectIfExists(snapValue->PrimitiveValueId);
if(rObj != nullptr)
{
res = rObj;
}
else
{
switch(snapValue->SnapType->JsTypeId)
{
case Js::TypeIds_Undefined:
res = jslib->GetUndefined();
break;
case Js::TypeIds_Null:
res = jslib->GetNull();
break;
case Js::TypeIds_Boolean:
res = (snapValue->u_boolValue ? jslib->GetTrue() : jslib->GetFalse());
break;
case Js::TypeIds_Number:
res = Js::JavascriptNumber::ToVarWithCheck(snapValue->u_doubleValue, ctx);
break;
case Js::TypeIds_Int64Number:
res = Js::JavascriptInt64Number::ToVar(snapValue->u_int64Value, ctx);
break;
case Js::TypeIds_UInt64Number:
res = Js::JavascriptUInt64Number::ToVar(snapValue->u_uint64Value, ctx);
break;
case Js::TypeIds_String:
res = Js::JavascriptString::NewCopyBuffer(snapValue->u_stringValue->Contents, snapValue->u_stringValue->Length, ctx);
break;
case Js::TypeIds_Symbol:
res = jslib->CreatePrimitveSymbol_TTD(snapValue->u_propertyIdValue);
break;
default:
TTDAssert(false, "These are supposed to be primitive values e.g., no pointers or properties.");
res = nullptr;
}
}
}
inflator->AddObject(snapValue->PrimitiveValueId, Js::RecyclableObject::FromVar(res));
}
void EmitSnapPrimitiveValue(const SnapPrimitiveValue* snapValue, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteAddr(NSTokens::Key::primitiveId, snapValue->PrimitiveValueId);
writer->WriteAddr(NSTokens::Key::typeId, snapValue->SnapType->TypePtrId, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::isWellKnownToken, snapValue->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN, NSTokens::Separator::CommaSeparator);
if(snapValue->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN)
{
writer->WriteWellKnownToken(NSTokens::Key::wellKnownToken, snapValue->OptWellKnownToken, NSTokens::Separator::CommaSeparator);
}
else
{
switch(snapValue->SnapType->JsTypeId)
{
case Js::TypeIds_Undefined:
case Js::TypeIds_Null:
break;
case Js::TypeIds_Boolean:
writer->WriteBool(NSTokens::Key::boolVal, !!snapValue->u_boolValue, NSTokens::Separator::CommaSeparator);
break;
case Js::TypeIds_Number:
writer->WriteDouble(NSTokens::Key::doubleVal, snapValue->u_doubleValue, NSTokens::Separator::CommaSeparator);
break;
case Js::TypeIds_Int64Number:
writer->WriteInt64(NSTokens::Key::i64Val, snapValue->u_int64Value, NSTokens::Separator::CommaSeparator);
break;
case Js::TypeIds_UInt64Number:
writer->WriteUInt64(NSTokens::Key::u64Val, snapValue->u_uint64Value, NSTokens::Separator::CommaSeparator);
break;
case Js::TypeIds_String:
writer->WriteString(NSTokens::Key::stringVal, *(snapValue->u_stringValue), NSTokens::Separator::CommaSeparator);
break;
case Js::TypeIds_Symbol:
writer->WriteInt32(NSTokens::Key::propertyId, snapValue->u_propertyIdValue, NSTokens::Separator::CommaSeparator);
break;
default:
TTDAssert(false, "These are supposed to be primitive values e.g., no pointers or properties.");
break;
}
}
writer->WriteRecordEnd();
}
void ParseSnapPrimitiveValue(SnapPrimitiveValue* snapValue, bool readSeperator, FileReader* reader, SlabAllocator& alloc, const TTDIdentifierDictionary<TTD_PTR_ID, NSSnapType::SnapType*>& ptrIdToTypeMap)
{
reader->ReadRecordStart(readSeperator);
snapValue->PrimitiveValueId = reader->ReadAddr(NSTokens::Key::primitiveId);
TTD_PTR_ID snapTypeId = reader->ReadAddr(NSTokens::Key::typeId, true);
snapValue->SnapType = ptrIdToTypeMap.LookupKnownItem(snapTypeId);
bool isWellKnown = reader->ReadBool(NSTokens::Key::isWellKnownToken, true);
if(isWellKnown)
{
snapValue->OptWellKnownToken = reader->ReadWellKnownToken(NSTokens::Key::wellKnownToken, alloc, true);
//just clear it to help avoid any confusion later
snapValue->u_uint64Value = 0;
}
else
{
snapValue->OptWellKnownToken = nullptr;
switch(snapValue->SnapType->JsTypeId)
{
case Js::TypeIds_Undefined:
case Js::TypeIds_Null:
break;
case Js::TypeIds_Boolean:
snapValue->u_boolValue = reader->ReadBool(NSTokens::Key::boolVal, true);
break;
case Js::TypeIds_Number:
snapValue->u_doubleValue = reader->ReadDouble(NSTokens::Key::doubleVal, true);
break;
case Js::TypeIds_Int64Number:
snapValue->u_int64Value = reader->ReadInt64(NSTokens::Key::i64Val, true);
break;
case Js::TypeIds_UInt64Number:
snapValue->u_uint64Value = reader->ReadUInt64(NSTokens::Key::u64Val, true);
break;
case Js::TypeIds_String:
snapValue->u_stringValue = alloc.SlabAllocateStruct<TTString>();
reader->ReadString(NSTokens::Key::stringVal, alloc, *(snapValue->u_stringValue), true);
break;
case Js::TypeIds_Symbol:
snapValue->u_propertyIdValue = (Js::PropertyId)reader->ReadInt32(NSTokens::Key::propertyId, true);
break;
default:
TTDAssert(false, "These are supposed to be primitive values e.g., no pointers or properties.");
break;
}
}
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SnapPrimitiveValue* v1, const SnapPrimitiveValue* v2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(v1->SnapType->JsTypeId == v2->SnapType->JsTypeId);
NSSnapType::AssertSnapEquiv(v1->SnapType, v2->SnapType, compareMap);
if(v1->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN || v2->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN)
{
compareMap.DiagnosticAssert(v1->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN && v2->OptWellKnownToken != TTD_INVALID_WELLKNOWN_TOKEN);
compareMap.DiagnosticAssert(TTD_DIAGNOSTIC_COMPARE_WELLKNOWN_TOKENS(v1->OptWellKnownToken, v2->OptWellKnownToken));
}
else
{
switch(v1->SnapType->JsTypeId)
{
case Js::TypeIds_Undefined:
case Js::TypeIds_Null:
break;
case Js::TypeIds_Boolean:
compareMap.DiagnosticAssert((!!v1->u_boolValue) == (!!v2->u_boolValue));
break;
case Js::TypeIds_Number:
compareMap.DiagnosticAssert(CheckSnapEquivTTDDouble(v1->u_doubleValue, v2->u_doubleValue));
break;
case Js::TypeIds_Int64Number:
compareMap.DiagnosticAssert(v1->u_int64Value == v2->u_int64Value);
break;
case Js::TypeIds_UInt64Number:
compareMap.DiagnosticAssert(v1->u_uint64Value == v2->u_uint64Value);
break;
case Js::TypeIds_String:
compareMap.DiagnosticAssert(TTStringEQForDiagnostics(*(v1->u_stringValue), *(v2->u_stringValue)));
break;
case Js::TypeIds_Symbol:
compareMap.DiagnosticAssert(v1->u_propertyIdValue == v2->u_propertyIdValue);
break;
default:
TTDAssert(false, "These are supposed to be primitive values e.g., no pointers or properties.");
break;
}
}
}
#endif
//////////////////
Js::Var* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator)
{
Js::ScriptContext* ctx = inflator->LookupScriptContext(slotInfo->ScriptContextLogId);
Field(Js::Var)* slotArray = RecyclerNewArray(ctx->GetRecycler(), Field(Js::Var), slotInfo->SlotCount + Js::ScopeSlots::FirstSlotIndex);
Js::ScopeSlots scopeSlots((Js::Var*)slotArray);
scopeSlots.SetCount(slotInfo->SlotCount);
Js::Var undef = ctx->GetLibrary()->GetUndefined();
for(uint32 j = 0; j < slotInfo->SlotCount; j++)
{
scopeSlots.Set(j, undef);
}
if(slotInfo->isFunctionBodyMetaData)
{
Js::FunctionBody* fbody = inflator->LookupFunctionBody(slotInfo->OptFunctionBodyId);
scopeSlots.SetScopeMetadata(fbody->GetFunctionInfo());
//This is a doubly nested lookup so if the scope slot array is large this could be expensive
Js::PropertyId* propertyIds = fbody->GetPropertyIdsForScopeSlotArray();
for(uint32 j = 0; j < slotInfo->SlotCount; j++)
{
Js::PropertyId trgtPid = slotInfo->PIDArray[j];
for(uint32 i = 0; i < fbody->GetScopeSlotArraySize(); i++)
{
if(trgtPid == propertyIds[i])
{
Js::Var sval = inflator->InflateTTDVar(slotInfo->Slots[j]);
scopeSlots.Set(i, sval);
break;
}
}
}
}
else
{
Js::DebuggerScope* dbgScope = nullptr;
if(slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN)
{
dbgScope = ctx->TTDWellKnownInfo->LookupKnownDebuggerScopeFromPath(slotInfo->OptWellKnownDbgScope);
}
else
{
Js::FunctionBody* scopeBody = nullptr;
int32 scopeIndex = -1;
inflator->LookupInfoForDebugScope(slotInfo->OptDebugScopeId, &scopeBody, &scopeIndex);
dbgScope = scopeBody->GetScopeObjectChain()->pScopeChain->Item(scopeIndex);
}
scopeSlots.SetScopeMetadata(dbgScope);
//This is a doubly nested lookup so if the scope slot array is large this could be expensive
for(uint32 j = 0; j < slotInfo->SlotCount; j++)
{
Js::PropertyId trgtPid = slotInfo->PIDArray[j];
for(uint32 i = 0; i < slotInfo->SlotCount; i++)
{
if(trgtPid == dbgScope->GetPropertyIdForSlotIndex_TTD(i))
{
Js::Var sval = inflator->InflateTTDVar(slotInfo->Slots[j]);
scopeSlots.Set(i, sval);
break;
}
}
}
}
return (Js::Var*)slotArray;
}
void EmitSlotArrayInfo(const SlotArrayInfo* slotInfo, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->AdjustIndent(1);
writer->WriteAddr(NSTokens::Key::slotId, slotInfo->SlotId, NSTokens::Separator::BigSpaceSeparator);
writer->WriteLogTag(NSTokens::Key::ctxTag, slotInfo->ScriptContextLogId, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::isFunctionMetaData, slotInfo->isFunctionBodyMetaData, NSTokens::Separator::CommaSeparator);
if(slotInfo->isFunctionBodyMetaData)
{
writer->WriteAddr(NSTokens::Key::functionBodyId, slotInfo->OptFunctionBodyId, NSTokens::Separator::CommaSeparator);
}
else
{
writer->WriteBool(NSTokens::Key::isWellKnownToken, slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN, NSTokens::Separator::CommaSeparator);
if(slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN)
{
writer->WriteWellKnownToken(NSTokens::Key::wellKnownToken, slotInfo->OptWellKnownDbgScope, NSTokens::Separator::CommaSeparator);
}
else
{
writer->WriteAddr(NSTokens::Key::debuggerScopeId, slotInfo->OptDebugScopeId, NSTokens::Separator::CommaSeparator);
}
}
writer->WriteLengthValue(slotInfo->SlotCount, NSTokens::Separator::CommaAndBigSpaceSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaAndBigSpaceSeparator);
writer->AdjustIndent(1);
for(uint32 i = 0; i < slotInfo->SlotCount; ++i)
{
writer->WriteRecordStart(i != 0 ? NSTokens::Separator::CommaAndBigSpaceSeparator : NSTokens::Separator::BigSpaceSeparator);
writer->WriteUInt32(NSTokens::Key::pid, slotInfo->PIDArray[i], NSTokens::Separator::NoSeparator);
writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
EmitTTDVar(slotInfo->Slots[i], writer, NSTokens::Separator::NoSeparator);
writer->WriteRecordEnd();
}
writer->AdjustIndent(-1);
writer->WriteSequenceEnd(NSTokens::Separator::BigSpaceSeparator);
writer->AdjustIndent(-1);
writer->WriteRecordEnd(NSTokens::Separator::BigSpaceSeparator);
}
void ParseSlotArrayInfo(SlotArrayInfo* slotInfo, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
slotInfo->SlotId = reader->ReadAddr(NSTokens::Key::slotId);
slotInfo->ScriptContextLogId = reader->ReadLogTag(NSTokens::Key::ctxTag, true);
slotInfo->isFunctionBodyMetaData = reader->ReadBool(NSTokens::Key::isFunctionMetaData, true);
slotInfo->OptFunctionBodyId = TTD_INVALID_PTR_ID;
slotInfo->OptDebugScopeId = TTD_INVALID_PTR_ID;
slotInfo->OptWellKnownDbgScope = TTD_INVALID_WELLKNOWN_TOKEN;
if(slotInfo->isFunctionBodyMetaData)
{
slotInfo->OptFunctionBodyId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
}
else
{
bool isWellKnown = reader->ReadBool(NSTokens::Key::isWellKnownToken, true);
if(isWellKnown)
{
slotInfo->OptWellKnownDbgScope = reader->ReadWellKnownToken(NSTokens::Key::wellKnownToken, alloc, true);
}
else
{
slotInfo->OptDebugScopeId = reader->ReadAddr(NSTokens::Key::debuggerScopeId, true);
}
}
slotInfo->SlotCount = reader->ReadLengthValue(true);
reader->ReadSequenceStart_WDefaultKey(true);
slotInfo->Slots = alloc.SlabAllocateArray<TTDVar>(slotInfo->SlotCount);
slotInfo->PIDArray = alloc.SlabAllocateArray<Js::PropertyId>(slotInfo->SlotCount);
for(uint32 i = 0; i < slotInfo->SlotCount; ++i)
{
reader->ReadRecordStart(i != 0);
slotInfo->PIDArray[i] = (Js::PropertyId)reader->ReadUInt32(NSTokens::Key::pid);
reader->ReadKey(NSTokens::Key::entry, true);
slotInfo->Slots[i] = ParseTTDVar(false, reader);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SlotArrayInfo* sai1, const SlotArrayInfo* sai2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(sai1->ScriptContextLogId == sai2->ScriptContextLogId);
compareMap.DiagnosticAssert(sai1->isFunctionBodyMetaData == sai2->isFunctionBodyMetaData);
if(sai1->isFunctionBodyMetaData)
{
compareMap.CheckConsistentAndAddPtrIdMapping_FunctionBody(sai1->OptFunctionBodyId, sai2->OptFunctionBodyId);
}
else
{
//TODO: emit debugger scope metadata
}
compareMap.DiagnosticAssert(sai1->SlotCount == sai2->SlotCount);
for(uint32 i = 0; i < sai1->SlotCount; ++i)
{
Js::PropertyId id1 = sai1->PIDArray[i];
bool found = false;
for(uint32 j = 0; j < sai1->SlotCount; ++j)
{
if(id1 == sai2->PIDArray[j])
{
AssertSnapEquivTTDVar_SlotArray(sai1->Slots[i], sai2->Slots[j], compareMap, i);
found = true;
break;
}
}
//TODO: We see this hit in a case where record has all values in a slot array when replaying --replay-debug (but not --replay).
// In the debug version the propertyId is in a Js::DebuggerScopeProperty instead. So this needs to be investigated in both extract and inflate.
compareMap.DiagnosticAssert(found);
}
}
#endif
Js::FrameDisplay* InflateScriptFunctionScopeInfo(const ScriptFunctionScopeInfo* funcScopeInfo, InflateMap* inflator)
{
Js::ScriptContext* ctx = inflator->LookupScriptContext(funcScopeInfo->ScriptContextLogId);
Js::FrameDisplay* environment = RecyclerNewPlus(ctx->GetRecycler(), funcScopeInfo->ScopeCount * sizeof(Js::Var), Js::FrameDisplay, funcScopeInfo->ScopeCount);
for(uint16 i = 0; i < funcScopeInfo->ScopeCount; ++i)
{
const ScopeInfoEntry& scp = funcScopeInfo->ScopeArray[i];
switch(scp.Tag)
{
case Js::ScopeType::ScopeType_ActivationObject:
case Js::ScopeType::ScopeType_WithScope:
{
Js::Var sval = inflator->LookupObject(scp.IDValue);
environment->SetItem(i, sval);
break;
}
case Js::ScopeType::ScopeType_SlotArray:
{
Js::Var* saval = inflator->LookupSlotArray(scp.IDValue);
environment->SetItem(i, saval);
break;
}
default:
TTDAssert(false, "Unknown scope kind");
break;
}
}
return environment;
}
void EmitScriptFunctionScopeInfo(const ScriptFunctionScopeInfo* funcScopeInfo, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteAddr(NSTokens::Key::scopeId, funcScopeInfo->ScopeId);
writer->WriteLogTag(NSTokens::Key::ctxTag, funcScopeInfo->ScriptContextLogId, NSTokens::Separator::CommaSeparator);
writer->WriteUInt32(NSTokens::Key::count, funcScopeInfo->ScopeCount, NSTokens::Separator::CommaSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < funcScopeInfo->ScopeCount; ++i)
{
writer->WriteRecordStart(i != 0 ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
writer->WriteTag<Js::ScopeType>(NSTokens::Key::scopeType, funcScopeInfo->ScopeArray[i].Tag);
writer->WriteAddr(NSTokens::Key::subscopeId, funcScopeInfo->ScopeArray[i].IDValue, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
writer->WriteSequenceEnd();
writer->WriteRecordEnd();
}
void ParseScriptFunctionScopeInfo(ScriptFunctionScopeInfo* funcScopeInfo, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
funcScopeInfo->ScopeId = reader->ReadAddr(NSTokens::Key::scopeId);
funcScopeInfo->ScriptContextLogId = reader->ReadLogTag(NSTokens::Key::ctxTag, true);
funcScopeInfo->ScopeCount = (uint16)reader->ReadUInt32(NSTokens::Key::count, true);
reader->ReadSequenceStart_WDefaultKey(true);
funcScopeInfo->ScopeArray = alloc.SlabAllocateArray<ScopeInfoEntry>(funcScopeInfo->ScopeCount);
for(uint32 i = 0; i < funcScopeInfo->ScopeCount; ++i)
{
reader->ReadRecordStart(i != 0);
funcScopeInfo->ScopeArray[i].Tag = reader->ReadTag<Js::ScopeType>(NSTokens::Key::scopeType);
funcScopeInfo->ScopeArray[i].IDValue = reader->ReadAddr(NSTokens::Key::subscopeId, true);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const ScriptFunctionScopeInfo* funcScopeInfo1, const ScriptFunctionScopeInfo* funcScopeInfo2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(funcScopeInfo1->ScriptContextLogId == funcScopeInfo2->ScriptContextLogId);
compareMap.DiagnosticAssert(funcScopeInfo1->ScopeCount == funcScopeInfo2->ScopeCount);
for(uint32 i = 0; i < funcScopeInfo1->ScopeCount; ++i)
{
compareMap.DiagnosticAssert(funcScopeInfo1->ScopeArray[i].Tag == funcScopeInfo2->ScopeArray[i].Tag);
compareMap.CheckConsistentAndAddPtrIdMapping_Scope(funcScopeInfo1->ScopeArray[i].IDValue, funcScopeInfo2->ScopeArray[i].IDValue, i);
}
}
#endif
//////////////////
Js::JavascriptPromiseCapability* InflatePromiseCapabilityInfo(const SnapPromiseCapabilityInfo* capabilityInfo, Js::ScriptContext* ctx, InflateMap* inflator)
{
if(!inflator->IsPromiseInfoDefined<Js::JavascriptPromiseCapability>(capabilityInfo->CapabilityId))
{
Js::Var promise = inflator->InflateTTDVar(capabilityInfo->PromiseVar);
Js::Var resolve = inflator->InflateTTDVar(capabilityInfo->ResolveVar);
Js::Var reject = inflator->InflateTTDVar(capabilityInfo->RejectVar);
Js::JavascriptPromiseCapability* res = ctx->GetLibrary()->CreatePromiseCapability_TTD(promise, resolve, reject);
inflator->AddInflatedPromiseInfo<Js::JavascriptPromiseCapability>(capabilityInfo->CapabilityId, res);
}
return inflator->LookupInflatedPromiseInfo<Js::JavascriptPromiseCapability>(capabilityInfo->CapabilityId);
}
void EmitPromiseCapabilityInfo(const SnapPromiseCapabilityInfo* capabilityInfo, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteAddr(NSTokens::Key::ptrIdVal, capabilityInfo->CapabilityId);
writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
EmitTTDVar(capabilityInfo->PromiseVar, writer, NSTokens::Separator::NoSeparator);
writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
EmitTTDVar(capabilityInfo->ResolveVar, writer, NSTokens::Separator::NoSeparator);
writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
EmitTTDVar(capabilityInfo->RejectVar, writer, NSTokens::Separator::NoSeparator);
writer->WriteRecordEnd();
}
void ParsePromiseCapabilityInfo(SnapPromiseCapabilityInfo* capabilityInfo, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
capabilityInfo->CapabilityId = reader->ReadAddr(NSTokens::Key::ptrIdVal);
reader->ReadKey(NSTokens::Key::entry, true);
capabilityInfo->PromiseVar = ParseTTDVar(false, reader);
reader->ReadKey(NSTokens::Key::entry, true);
capabilityInfo->ResolveVar = ParseTTDVar(false, reader);
reader->ReadKey(NSTokens::Key::entry, true);
capabilityInfo->RejectVar = ParseTTDVar(false, reader);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SnapPromiseCapabilityInfo* capabilityInfo1, const SnapPromiseCapabilityInfo* capabilityInfo2, TTDCompareMap& compareMap)
{
compareMap.CheckConsistentAndAddPtrIdMapping_NoEnqueue(capabilityInfo1->CapabilityId, capabilityInfo2->CapabilityId);
AssertSnapEquivTTDVar_Special(capabilityInfo1->PromiseVar, capabilityInfo2->PromiseVar, compareMap, _u("promiseVar"));
AssertSnapEquivTTDVar_Special(capabilityInfo1->ResolveVar, capabilityInfo2->ResolveVar, compareMap, _u("resolveObjId"));
AssertSnapEquivTTDVar_Special(capabilityInfo1->RejectVar, capabilityInfo2->RejectVar, compareMap, _u("rejectObjId"));
}
#endif
Js::JavascriptPromiseReaction* InflatePromiseReactionInfo(const SnapPromiseReactionInfo* reactionInfo, Js::ScriptContext* ctx, InflateMap* inflator)
{
if(!inflator->IsPromiseInfoDefined<Js::JavascriptPromiseReaction>(reactionInfo->PromiseReactionId))
{
Js::RecyclableObject* handler = inflator->LookupObject(reactionInfo->HandlerObjId);
Js::JavascriptPromiseCapability* capabilities = InflatePromiseCapabilityInfo(&reactionInfo->Capabilities, ctx, inflator);
Js::JavascriptPromiseReaction* res = ctx->GetLibrary()->CreatePromiseReaction_TTD(handler, capabilities);
inflator->AddInflatedPromiseInfo<Js::JavascriptPromiseReaction>(reactionInfo->PromiseReactionId, res);
}
return inflator->LookupInflatedPromiseInfo<Js::JavascriptPromiseReaction>(reactionInfo->PromiseReactionId);
}
void EmitPromiseReactionInfo(const SnapPromiseReactionInfo* reactionInfo, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteAddr(NSTokens::Key::ptrIdVal, reactionInfo->PromiseReactionId);
writer->WriteAddr(NSTokens::Key::ptrIdVal, reactionInfo->HandlerObjId, NSTokens::Separator::CommaSeparator);
writer->WriteKey(NSTokens::Key::entry, NSTokens::Separator::CommaSeparator);
EmitPromiseCapabilityInfo(&reactionInfo->Capabilities, writer, NSTokens::Separator::NoSeparator);
writer->WriteRecordEnd();
}
void ParsePromiseReactionInfo(SnapPromiseReactionInfo* reactionInfo, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
reactionInfo->PromiseReactionId = reader->ReadAddr(NSTokens::Key::ptrIdVal);
reactionInfo->HandlerObjId = reader->ReadAddr(NSTokens::Key::ptrIdVal, true);
reader->ReadKey(NSTokens::Key::entry, true);
ParsePromiseCapabilityInfo(&reactionInfo->Capabilities, false, reader, alloc);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SnapPromiseReactionInfo* reactionInfo1, const SnapPromiseReactionInfo* reactionInfo2, TTDCompareMap& compareMap)
{
compareMap.CheckConsistentAndAddPtrIdMapping_NoEnqueue(reactionInfo1->PromiseReactionId, reactionInfo2->PromiseReactionId);
compareMap.CheckConsistentAndAddPtrIdMapping_Special(reactionInfo1->HandlerObjId, reactionInfo2->HandlerObjId, _u("handlerObjId"));
AssertSnapEquiv(&(reactionInfo1->Capabilities), &(reactionInfo2->Capabilities), compareMap);
}
#endif
//////////////////
void ExtractSnapFunctionBodyScopeChain(bool isWellKnownFunction, SnapFunctionBodyScopeChain& scopeChain, Js::FunctionBody* fb, SlabAllocator& alloc)
{
scopeChain.ScopeCount = 0;
scopeChain.ScopeArray = nullptr;
if(!isWellKnownFunction && fb->GetScopeObjectChain() != nullptr)
{
Js::ScopeObjectChain* scChain = fb->GetScopeObjectChain();
scopeChain.ScopeCount = (uint32)scChain->pScopeChain->Count();
if(scopeChain.ScopeCount == 0)
{
scopeChain.ScopeArray = nullptr;
}
else
{
scopeChain.ScopeArray = alloc.SlabAllocateArray<TTD_PTR_ID>(scopeChain.ScopeCount);
for(int32 i = 0; i < scChain->pScopeChain->Count(); ++i)
{
Js::DebuggerScope* dbgScope = scChain->pScopeChain->Item(i);
scopeChain.ScopeArray[i] = TTD_CONVERT_DEBUGSCOPE_TO_PTR_ID(dbgScope);
}
}
}
}
void EmitSnapFunctionBodyScopeChain(const SnapFunctionBodyScopeChain& scopeChain, FileWriter* writer)
{
writer->WriteRecordStart();
writer->WriteLengthValue(scopeChain.ScopeCount);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < scopeChain.ScopeCount; ++i)
{
writer->WriteNakedAddr(scopeChain.ScopeArray[i], (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
}
writer->WriteSequenceEnd();
writer->WriteRecordEnd();
}
void ParseSnapFunctionBodyScopeChain(SnapFunctionBodyScopeChain& scopeChain, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart();
scopeChain.ScopeCount = reader->ReadLengthValue();
scopeChain.ScopeArray = (scopeChain.ScopeCount != 0) ? alloc.SlabAllocateArray<TTD_PTR_ID>(scopeChain.ScopeCount) : nullptr;
reader->ReadSequenceStart_WDefaultKey(true);
for(uint32 i = 0; i < scopeChain.ScopeCount; ++i)
{
scopeChain.ScopeArray[i] = reader->ReadNakedAddr(i != 0);
}
reader->ReadSequenceEnd();
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SnapFunctionBodyScopeChain& chain1, const SnapFunctionBodyScopeChain& chain2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(chain1.ScopeCount == chain2.ScopeCount);
//Not sure if there is a way to compare the pointer ids in the two scopes
}
#endif
//////////////////
void ExtractTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, uint64 sourceContextId, bool isUtf8source, const byte* source, uint32 sourceLen, SlabAllocator& alloc)
{
fbInfo->ScriptContextLogId = fb->GetScriptContext()->ScriptContextLogTag;
fbInfo->TopLevelBodyCtr = topLevelCtr;
alloc.CopyNullTermStringInto(fb->GetDisplayName(), fbInfo->FunctionName);
fbInfo->ModuleId = moduleId;
fbInfo->SourceContextId = sourceContextId;
alloc.CopyNullTermStringInto(fb->GetSourceContextInfo()->url, fbInfo->SourceUri);
fbInfo->IsUtf8 = isUtf8source;
fbInfo->ByteLength = sourceLen;
fbInfo->SourceBuffer = alloc.SlabAllocateArray<byte>(fbInfo->ByteLength);
js_memcpy_s(fbInfo->SourceBuffer, fbInfo->ByteLength, source, sourceLen);
fbInfo->DbgSerializedBytecodeSize = 0;
fbInfo->DbgSerializedBytecodeBuffer = nullptr;
ExtractSnapFunctionBodyScopeChain(false, fbInfo->ScopeChainInfo, fb, alloc);
}
void EmitTopLevelCommonBodyResolveInfo(const TopLevelCommonBodyResolveInfo* fbInfo, bool emitInline, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteUInt32(NSTokens::Key::functionBodyId, fbInfo->TopLevelBodyCtr);
writer->WriteLogTag(NSTokens::Key::ctxTag, fbInfo->ScriptContextLogId, NSTokens::Separator::CommaSeparator);
writer->WriteString(NSTokens::Key::name, fbInfo->FunctionName, NSTokens::Separator::CommaSeparator);
writer->WriteUInt64(NSTokens::Key::moduleId, fbInfo->ModuleId, NSTokens::Separator::CommaSeparator);
writer->WriteUInt64(NSTokens::Key::sourceContextId, fbInfo->SourceContextId, NSTokens::Separator::CommaSeparator);
writer->WriteString(NSTokens::Key::uri, fbInfo->SourceUri, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::boolVal, fbInfo->IsUtf8, NSTokens::Separator::CommaSeparator);
writer->WriteLengthValue(fbInfo->ByteLength, NSTokens::Separator::CommaSeparator);
writer->WriteKey(NSTokens::Key::scopeChain, NSTokens::Separator::CommaSeparator);
EmitSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, writer);
if(emitInline || IsNullPtrTTString(fbInfo->SourceUri))
{
TTDAssert(!fbInfo->IsUtf8, "Should only emit char16 encoded data in inline mode.");
writer->WriteInlineCode((char16*)fbInfo->SourceBuffer, fbInfo->ByteLength / sizeof(char16), NSTokens::Separator::CommaSeparator);
}
else
{
JsSupport::WriteCodeToFile(threadContext, false, fbInfo->TopLevelBodyCtr, fbInfo->IsUtf8, fbInfo->SourceBuffer, fbInfo->ByteLength);
}
}
void ParseTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, bool readSeperator, bool parseInline, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
fbInfo->TopLevelBodyCtr = reader->ReadUInt32(NSTokens::Key::functionBodyId);
fbInfo->ScriptContextLogId = reader->ReadLogTag(NSTokens::Key::ctxTag, true);
reader->ReadString(NSTokens::Key::name, alloc, fbInfo->FunctionName, true);
fbInfo->ModuleId = (Js::ModuleID)reader->ReadUInt64(NSTokens::Key::moduleId, true);
fbInfo->SourceContextId = reader->ReadUInt64(NSTokens::Key::sourceContextId, true);
reader->ReadString(NSTokens::Key::uri, alloc, fbInfo->SourceUri, true);
fbInfo->IsUtf8 = reader->ReadBool(NSTokens::Key::boolVal, true);
fbInfo->ByteLength = reader->ReadLengthValue(true);
fbInfo->SourceBuffer = alloc.SlabAllocateArray<byte>(fbInfo->ByteLength);
reader->ReadKey(NSTokens::Key::scopeChain, true);
ParseSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, reader, alloc);
if(parseInline || IsNullPtrTTString(fbInfo->SourceUri))
{
TTDAssert(!fbInfo->IsUtf8, "Should only emit char16 encoded data in inline mode.");
reader->ReadInlineCode((char16*)fbInfo->SourceBuffer, fbInfo->ByteLength / sizeof(char16), true);
}
else
{
JsSupport::ReadCodeFromFile(threadContext, false, fbInfo->TopLevelBodyCtr, fbInfo->IsUtf8, fbInfo->SourceBuffer, fbInfo->ByteLength);
}
fbInfo->DbgSerializedBytecodeSize = 0;
fbInfo->DbgSerializedBytecodeBuffer = nullptr;
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const TopLevelCommonBodyResolveInfo* fbInfo1, const TopLevelCommonBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(fbInfo1->ScriptContextLogId == fbInfo2->ScriptContextLogId);
compareMap.DiagnosticAssert(fbInfo1->TopLevelBodyCtr == fbInfo2->TopLevelBodyCtr);
compareMap.DiagnosticAssert(TTStringEQForDiagnostics(fbInfo1->FunctionName, fbInfo2->FunctionName));
compareMap.DiagnosticAssert(fbInfo1->ModuleId == fbInfo2->ModuleId);
compareMap.DiagnosticAssert(fbInfo1->SourceContextId == fbInfo2->SourceContextId);
compareMap.DiagnosticAssert(TTStringEQForDiagnostics(fbInfo1->SourceUri, fbInfo2->SourceUri));
compareMap.DiagnosticAssert(fbInfo1->IsUtf8 == fbInfo2->IsUtf8);
compareMap.DiagnosticAssert(fbInfo1->ByteLength == fbInfo2->ByteLength);
for(uint32 i = 0; i < fbInfo1->ByteLength; ++i)
{
compareMap.DiagnosticAssert(fbInfo1->SourceBuffer[i] == fbInfo2->SourceBuffer[i]);
}
AssertSnapEquiv(fbInfo1->ScopeChainInfo, fbInfo2->ScopeChainInfo, compareMap);
}
#endif
////
//Regular script-load functions
void ExtractTopLevelLoadedFunctionBodyInfo(TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, uint64 sourceContextId, bool isUtf8, const byte* source, uint32 sourceLen, LoadScriptFlag loadFlag, SlabAllocator& alloc)
{
NSSnapValues::ExtractTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, fb, topLevelCtr, moduleId, sourceContextId, isUtf8, source, sourceLen, alloc);
fbInfo->LoadFlag = loadFlag;
}
Js::FunctionBody* InflateTopLevelLoadedFunctionBodyInfo(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx)
{
byte* script = fbInfo->TopLevelBase.SourceBuffer;
uint32 scriptLength = fbInfo->TopLevelBase.ByteLength;
uint64 sourceContext = fbInfo->TopLevelBase.SourceContextId;
TTDAssert(ctx->GetSourceContextInfo((DWORD_PTR)sourceContext, nullptr) == nullptr, "On inflate we should either have clean ctxts or we want to optimize the inflate process by skipping redoing this work!!!");
TTDAssert(fbInfo->TopLevelBase.IsUtf8 == ((fbInfo->LoadFlag & LoadScriptFlag_Utf8Source) == LoadScriptFlag_Utf8Source), "Utf8 status is inconsistent!!!");
const char16* srcUri = fbInfo->TopLevelBase.SourceUri.Contents;
uint32 srcUriLength = fbInfo->TopLevelBase.SourceUri.Length;
SourceContextInfo * sourceContextInfo = ctx->CreateSourceContextInfo((DWORD_PTR)sourceContext, srcUri, srcUriLength, nullptr);
TTDAssert(fbInfo->TopLevelBase.IsUtf8 || sizeof(wchar) == sizeof(char16), "Non-utf8 code only allowed on windows!!!");
const int chsize = (fbInfo->LoadFlag & LoadScriptFlag_Utf8Source) ? sizeof(char) : sizeof(char16);
SRCINFO si = {
/* sourceContextInfo */ sourceContextInfo,
/* dlnHost */ 0,
/* ulColumnHost */ 0,
/* lnMinHost */ 0,
/* ichMinHost */ 0,
/* ichLimHost */ static_cast<ULONG>(scriptLength / chsize), // OK to truncate since this is used to limit sourceText in debugDocument/compilation errors.
/* ulCharOffset */ 0,
/* mod */ fbInfo->TopLevelBase.ModuleId,
/* grfsi */ 0
};
Js::Utf8SourceInfo* utf8SourceInfo = nullptr;
CompileScriptException se;
Js::JavascriptFunction* scriptFunction = nullptr;
Js::FunctionBody* globalBody = nullptr;
if(fbInfo->TopLevelBase.DbgSerializedBytecodeSize != 0)
{
//
//TODO: Bytecode serializer does not support debug bytecode (StatementMaps vs Positions) so add this to serializer code.
// Then we can add code do optimized bytecode reload here.
//
TTDAssert(false, "Not implemented yet -- this branch should never be taken.");
}
else
{
scriptFunction = ctx->LoadScript(script, scriptLength, &si, &se, &utf8SourceInfo, Js::Constants::GlobalCode, fbInfo->LoadFlag, nullptr);
TTDAssert(scriptFunction != nullptr, "Something went wrong");
globalBody = TTD::JsSupport::ForceAndGetFunctionBody(scriptFunction->GetParseableFunctionInfo());
//
//TODO: Bytecode serializer does not suppper debug bytecode (StatementMaps vs Positions) so add this to serializer code.
// Then we can add code to do optimized bytecode reload here.
//
}
////
//We don't do this automatically in the load script helper so do it here
BEGIN_JS_RUNTIME_CALL(ctx);
{
ctx->TTDContextInfo->ProcessFunctionBodyOnLoad(globalBody, nullptr);
ctx->TTDContextInfo->RegisterLoadedScript(globalBody, fbInfo->TopLevelBase.TopLevelBodyCtr);
globalBody->GetUtf8SourceInfo()->SetSourceInfoForDebugReplay_TTD(fbInfo->TopLevelBase.TopLevelBodyCtr);
}
END_JS_RUNTIME_CALL(ctx);
bool isLibraryCode = ((fbInfo->LoadFlag & LoadScriptFlag_LibraryCode) == LoadScriptFlag_LibraryCode);
if(ctx->GetThreadContext()->TTDExecutionInfo != nullptr && !isLibraryCode)
{
ctx->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad(ctx, fbInfo->TopLevelBase.TopLevelBodyCtr, globalBody, utf8SourceInfo, &se);
}
////
return globalBody;
}
void EmitTopLevelLoadedFunctionBodyInfo(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator)
{
NSSnapValues::EmitTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, false, threadContext, writer, separator);
writer->WriteTag<LoadScriptFlag>(NSTokens::Key::loadFlag, fbInfo->LoadFlag, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
void ParseTopLevelLoadedFunctionBodyInfo(TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, bool readSeperator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc)
{
NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeperator, false, threadContext, reader, alloc);
fbInfo->LoadFlag = reader->ReadTag<LoadScriptFlag>(NSTokens::Key::loadFlag, true);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo1, const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(fbInfo1->LoadFlag == fbInfo2->LoadFlag);
AssertSnapEquiv(&(fbInfo1->TopLevelBase), &(fbInfo2->TopLevelBase), compareMap);
}
#endif
////
//'new Function(...)' functions
void ExtractTopLevelNewFunctionBodyInfo(TopLevelNewFunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, const char16* source, uint32 sourceLen, SlabAllocator& alloc)
{
NSSnapValues::ExtractTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, fb, topLevelCtr, moduleId, 0, false, (const byte*)source, sourceLen * sizeof(char16), alloc);
}
Js::FunctionBody* InflateTopLevelNewFunctionBodyInfo(const TopLevelNewFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx)
{
// Bug 1105479. Get the module id from the caller
Js::ModuleID moduleID = kmodGlobal;
BOOL strictMode = FALSE;
char16* source = (char16*)fbInfo->TopLevelBase.SourceBuffer;
int32 length = (int32)(fbInfo->TopLevelBase.ByteLength / sizeof(char16));
Js::JavascriptFunction* pfuncScript = ctx->GetGlobalObject()->EvalHelper(ctx, source, length, moduleID, fscrNil, Js::Constants::FunctionCode, TRUE, TRUE, strictMode);
TTDAssert(pfuncScript != nullptr, "Something went wrong!!!");
// Indicate that this is a top-level function. We don't pass the fscrGlobalCode flag to the eval helper,
// or it will return the global function that wraps the declared function body, as though it were an eval.
// But we want, for instance, to be able to verify that we did the right amount of deferred parsing.
Js::ParseableFunctionInfo* functionInfo = pfuncScript->GetParseableFunctionInfo();
functionInfo->SetGrfscr(functionInfo->GetGrfscr() | fscrGlobalCode);
Js::FunctionBody* fb = JsSupport::ForceAndGetFunctionBody(pfuncScript->GetParseableFunctionInfo());
//We don't do this automatically in the eval helper so do it here
ctx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
ctx->TTDContextInfo->RegisterNewScript(fb, fbInfo->TopLevelBase.TopLevelBodyCtr);
return fb;
}
void EmitTopLevelNewFunctionBodyInfo(const TopLevelNewFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator)
{
NSSnapValues::EmitTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, true, threadContext, writer, separator);
writer->WriteRecordEnd();
}
void ParseTopLevelNewFunctionBodyInfo(TopLevelNewFunctionBodyResolveInfo* fbInfo, bool readSeperator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc)
{
NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeperator, false, threadContext, reader, alloc);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const TopLevelNewFunctionBodyResolveInfo* fbInfo1, const TopLevelNewFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap)
{
AssertSnapEquiv(&(fbInfo1->TopLevelBase), &(fbInfo2->TopLevelBase), compareMap);
}
#endif
////
//'eval(...)' functions
void ExtractTopLevelEvalFunctionBodyInfo(TopLevelEvalFunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, const char16* source, uint32 sourceLen, uint32 grfscr, bool registerDocument, BOOL isIndirect, BOOL strictMode, SlabAllocator& alloc)
{
NSSnapValues::ExtractTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, fb, topLevelCtr, moduleId, 0, false, (const byte*)source, sourceLen * sizeof(char16), alloc);
fbInfo->EvalFlags = grfscr;
fbInfo->RegisterDocument = registerDocument;
fbInfo->IsIndirect = !!isIndirect;
fbInfo->IsStrictMode = !!strictMode;
}
Js::FunctionBody* InflateTopLevelEvalFunctionBodyInfo(const TopLevelEvalFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx)
{
uint32 grfscr = ((uint32)fbInfo->EvalFlags) | fscrReturnExpression | fscrEval | fscrEvalCode | fscrGlobalCode;
char16* source = (char16*)fbInfo->TopLevelBase.SourceBuffer;
int32 sourceLen = (int32)(fbInfo->TopLevelBase.ByteLength / sizeof(char16));
Js::ScriptFunction* pfuncScript = ctx->GetLibrary()->GetGlobalObject()->EvalHelper(ctx, source, sourceLen, fbInfo->TopLevelBase.ModuleId, grfscr, Js::Constants::EvalCode, fbInfo->RegisterDocument, fbInfo->IsIndirect, fbInfo->IsStrictMode);
Assert(!pfuncScript->GetFunctionInfo()->IsGenerator());
Js::FastEvalMapString key(source, sourceLen, fbInfo->TopLevelBase.ModuleId, fbInfo->IsStrictMode, false);
ctx->AddToEvalMap(key, fbInfo->IsIndirect, pfuncScript);
Js::FunctionBody* fb = JsSupport::ForceAndGetFunctionBody(pfuncScript->GetParseableFunctionInfo());
//We don't do this automatically ing the eval helper so do it here
ctx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
ctx->TTDContextInfo->RegisterEvalScript(fb, fbInfo->TopLevelBase.TopLevelBodyCtr);
return fb;
}
void EmitTopLevelEvalFunctionBodyInfo(const TopLevelEvalFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator)
{
NSSnapValues::EmitTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, true, threadContext, writer, separator);
writer->WriteUInt64(NSTokens::Key::u64Val, fbInfo->EvalFlags, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::boolVal, fbInfo->RegisterDocument, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::boolVal, fbInfo->IsIndirect, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::boolVal, fbInfo->IsStrictMode, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
void ParseTopLevelEvalFunctionBodyInfo(TopLevelEvalFunctionBodyResolveInfo* fbInfo, bool readSeperator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc)
{
NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeperator, false, threadContext, reader, alloc);
fbInfo->EvalFlags = reader->ReadUInt64(NSTokens::Key::u64Val, true);
fbInfo->RegisterDocument = reader->ReadBool(NSTokens::Key::boolVal, true);
fbInfo->IsIndirect = reader->ReadBool(NSTokens::Key::boolVal, true);
fbInfo->IsStrictMode = reader->ReadBool(NSTokens::Key::boolVal, true);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const TopLevelEvalFunctionBodyResolveInfo* fbInfo1, const TopLevelEvalFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(fbInfo1->EvalFlags == fbInfo2->EvalFlags);
compareMap.DiagnosticAssert(fbInfo1->RegisterDocument == fbInfo2->RegisterDocument);
compareMap.DiagnosticAssert(fbInfo1->IsIndirect == fbInfo2->IsIndirect);
compareMap.DiagnosticAssert(fbInfo1->IsStrictMode == fbInfo2->IsStrictMode);
AssertSnapEquiv(&(fbInfo1->TopLevelBase), &(fbInfo2->TopLevelBase), compareMap);
}
#endif
void ExtractFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, bool isWellKnown, SlabAllocator& alloc)
{
fbInfo->FunctionBodyId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(fb);
fbInfo->ScriptContextLogId = fb->GetScriptContext()->ScriptContextLogTag;
alloc.CopyStringIntoWLength(fb->GetDisplayName(), fb->GetDisplayNameLength(), fbInfo->FunctionName);
TTDAssert(wcscmp(fbInfo->FunctionName.Contents, Js::Constants::GlobalCode) != 0, "Why are we snapshotting global code??");
if(isWellKnown)
{
fbInfo->OptKnownPath = alloc.CopyRawNullTerminatedStringInto(fb->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownFunctionBody(fb));
fbInfo->OptParentBodyId = TTD_INVALID_PTR_ID;
fbInfo->OptLine = -1;
fbInfo->OptColumn = -1;
}
else
{
fbInfo->OptKnownPath = TTD_INVALID_WELLKNOWN_TOKEN;
Js::FunctionBody* parentBody = fb->GetScriptContext()->TTDContextInfo->ResolveParentBody(fb);
TTDAssert(parentBody != nullptr, "We missed something!!!");
fbInfo->OptParentBodyId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(parentBody);
fbInfo->OptLine = fb->GetLineNumber();
fbInfo->OptColumn = fb->GetColumnNumber();
}
ExtractSnapFunctionBodyScopeChain(fbInfo->OptKnownPath != TTD_INVALID_WELLKNOWN_TOKEN, fbInfo->ScopeChainInfo, fb, alloc);
}
void InflateFunctionBody(const FunctionBodyResolveInfo* fbInfo, InflateMap* inflator, const TTDIdentifierDictionary<TTD_PTR_ID, FunctionBodyResolveInfo*>& idToFbResolveMap)
{
if(inflator->IsFunctionBodyAlreadyInflated(fbInfo->FunctionBodyId))
{
return;
}
Js::ScriptContext* ctx = inflator->LookupScriptContext(fbInfo->ScriptContextLogId);
Js::FunctionBody* resfb = nullptr;
if(fbInfo->OptKnownPath != TTD_INVALID_WELLKNOWN_TOKEN)
{
resfb = ctx->TTDWellKnownInfo->LookupKnownFunctionBodyFromPath(fbInfo->OptKnownPath);
}
else
{
resfb = inflator->FindReusableFunctionBodyIfExists(fbInfo->FunctionBodyId);
if(resfb == nullptr)
{
//Find and inflate the parent body
if(!inflator->IsFunctionBodyAlreadyInflated(fbInfo->OptParentBodyId))
{
const FunctionBodyResolveInfo* pBodyInfo = idToFbResolveMap.LookupKnownItem(fbInfo->OptParentBodyId);
InflateFunctionBody(pBodyInfo, inflator, idToFbResolveMap);
}
Js::FunctionBody* parentBody = inflator->LookupFunctionBody(fbInfo->OptParentBodyId);
//
//TODO: this is a potentially expensive linear search (but needed since classes dump implicit functions out-of-text order).
// May want to add sort and save in inflator or our shaddow info in script context if this is looking expensive.
//
uint32 blength = parentBody->GetNestedCount();
for(uint32 i = 0; i < blength; ++i)
{
Js::ParseableFunctionInfo* pfi = parentBody->GetNestedFunctionForExecution(i);
Js::FunctionBody* currfb = JsSupport::ForceAndGetFunctionBody(pfi);
if(fbInfo->OptLine == currfb->GetLineNumber() && fbInfo->OptColumn == currfb->GetColumnNumber())
{
resfb = currfb;
break;
}
}
TTDAssert(resfb != nullptr && fbInfo->OptLine == resfb->GetLineNumber() && fbInfo->OptColumn == resfb->GetColumnNumber(), "We are missing something");
TTDAssert(resfb != nullptr && (wcscmp(fbInfo->FunctionName.Contents, resfb->GetDisplayName()) == 0 || wcscmp(_u("get"), resfb->GetDisplayName()) == 0 || wcscmp(_u("set"), resfb->GetDisplayName()) == 0), "We are missing something");
}
//Make sure to register any scopes the found function body has (but *not* for well known functions)
inflator->UpdateFBScopes(fbInfo->ScopeChainInfo, resfb);
}
bool updateName = false;
if(fbInfo->FunctionName.Length != resfb->GetDisplayNameLength())
{
updateName = true;
}
else
{
const char16* fname = resfb->GetDisplayName();
for(uint32 i = 0; i < fbInfo->FunctionName.Length; ++i)
{
updateName |= (fbInfo->FunctionName.Contents[i] != fname[i]);
}
}
if(updateName)
{
uint32 suffixWDotPos = (fbInfo->FunctionName.Length - 4);
uint32 suffixPos = (fbInfo->FunctionName.Length - 3);
TTDAssert(wcsstr(fbInfo->FunctionName.Contents, _u(".get")) == (fbInfo->FunctionName.Contents + suffixWDotPos) || wcsstr(fbInfo->FunctionName.Contents, _u(".set")) == (fbInfo->FunctionName.Contents + suffixWDotPos), "Does not start with get or set");
resfb->SetDisplayName(fbInfo->FunctionName.Contents, fbInfo->FunctionName.Length, suffixPos, Js::FunctionProxy::SetDisplayNameFlagsRecyclerAllocated);
}
inflator->AddInflationFunctionBody(fbInfo->FunctionBodyId, resfb);
}
void EmitFunctionBodyInfo(const FunctionBodyResolveInfo* fbInfo, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteAddr(NSTokens::Key::functionBodyId, fbInfo->FunctionBodyId);
writer->WriteLogTag(NSTokens::Key::ctxTag, fbInfo->ScriptContextLogId, NSTokens::Separator::CommaSeparator);
writer->WriteString(NSTokens::Key::name, fbInfo->FunctionName, NSTokens::Separator::CommaSeparator);
writer->WriteBool(NSTokens::Key::isWellKnownToken, fbInfo->OptKnownPath != nullptr, NSTokens::Separator::CommaSeparator);
if(fbInfo->OptKnownPath != TTD_INVALID_WELLKNOWN_TOKEN)
{
writer->WriteWellKnownToken(NSTokens::Key::wellKnownToken, fbInfo->OptKnownPath, NSTokens::Separator::CommaSeparator);
}
else
{
writer->WriteAddr(NSTokens::Key::parentBodyId, fbInfo->OptParentBodyId, NSTokens::Separator::CommaSeparator);
writer->WriteInt64(NSTokens::Key::line, fbInfo->OptLine, NSTokens::Separator::CommaSeparator);
writer->WriteInt64(NSTokens::Key::column, fbInfo->OptColumn, NSTokens::Separator::CommaSeparator);
}
writer->WriteKey(NSTokens::Key::scopeChain, NSTokens::Separator::CommaSeparator);
EmitSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, writer);
writer->WriteRecordEnd();
}
void ParseFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
fbInfo->FunctionBodyId = reader->ReadAddr(NSTokens::Key::functionBodyId);
fbInfo->ScriptContextLogId = reader->ReadLogTag(NSTokens::Key::ctxTag, true);
reader->ReadString(NSTokens::Key::name, alloc, fbInfo->FunctionName, true);
bool isWellKnown = reader->ReadBool(NSTokens::Key::isWellKnownToken, true);
if(isWellKnown)
{
fbInfo->OptKnownPath = reader->ReadWellKnownToken(NSTokens::Key::wellKnownToken, alloc, true);
fbInfo->OptParentBodyId = TTD_INVALID_PTR_ID;
fbInfo->OptLine = -1;
fbInfo->OptColumn = -1;
}
else
{
fbInfo->OptKnownPath = TTD_INVALID_WELLKNOWN_TOKEN;
fbInfo->OptParentBodyId = reader->ReadAddr(NSTokens::Key::parentBodyId, true);
fbInfo->OptLine = reader->ReadInt64(NSTokens::Key::line, true);
fbInfo->OptColumn = reader->ReadInt64(NSTokens::Key::column, true);
}
reader->ReadKey(NSTokens::Key::scopeChain, true);
ParseSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, reader, alloc);
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const FunctionBodyResolveInfo* fbInfo1, const FunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(fbInfo1->ScriptContextLogId == fbInfo2->ScriptContextLogId);
compareMap.DiagnosticAssert(TTStringEQForDiagnostics(fbInfo1->FunctionName, fbInfo2->FunctionName));
compareMap.DiagnosticAssert(TTD_DIAGNOSTIC_COMPARE_WELLKNOWN_TOKENS(fbInfo1->OptKnownPath, fbInfo2->OptKnownPath));
if(fbInfo1->OptKnownPath == TTD_INVALID_WELLKNOWN_TOKEN)
{
compareMap.CheckConsistentAndAddPtrIdMapping_FunctionBody(fbInfo1->OptParentBodyId, fbInfo2->OptParentBodyId);
compareMap.DiagnosticAssert(fbInfo1->OptLine == fbInfo2->OptLine);
compareMap.DiagnosticAssert(fbInfo1->OptColumn == fbInfo2->OptColumn);
}
AssertSnapEquiv(fbInfo1->ScopeChainInfo, fbInfo1->ScopeChainInfo, compareMap);
}
#endif
//////////////////
void ExtractScriptContext(SnapContext* snapCtx, Js::ScriptContext* ctx, const JsUtil::BaseDictionary<Js::RecyclableObject*, TTD_LOG_PTR_ID, HeapAllocator>& objToLogIdMap, SlabAllocator& alloc)
{
snapCtx->ScriptContextLogId = ctx->ScriptContextLogTag;
snapCtx->IsPNRGSeeded = ctx->GetLibrary()->IsPRNGSeeded();
snapCtx->RandomSeed0 = ctx->GetLibrary()->GetRandSeed0();
snapCtx->RandomSeed1 = ctx->GetLibrary()->GetRandSeed1();
alloc.CopyNullTermStringInto(ctx->GetUrl(), snapCtx->ContextSRC);
JsUtil::List<TopLevelFunctionInContextRelation, HeapAllocator> topLevelScriptLoad(&HeapAllocator::Instance);
JsUtil::List<TopLevelFunctionInContextRelation, HeapAllocator> topLevelNewFunction(&HeapAllocator::Instance);
JsUtil::List<TopLevelFunctionInContextRelation, HeapAllocator> topLevelEval(&HeapAllocator::Instance);
ctx->TTDContextInfo->GetLoadedSources(topLevelScriptLoad, topLevelNewFunction, topLevelEval);
snapCtx->LoadedTopLevelScriptCount = topLevelScriptLoad.Count();
if(snapCtx->LoadedTopLevelScriptCount == 0)
{
snapCtx->LoadedTopLevelScriptArray = nullptr;
}
else
{
snapCtx->LoadedTopLevelScriptArray = alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(snapCtx->LoadedTopLevelScriptCount);
for(int32 i = 0; i < topLevelScriptLoad.Count(); ++i)
{
snapCtx->LoadedTopLevelScriptArray[i] = topLevelScriptLoad.Item(i);
}
}
snapCtx->NewFunctionTopLevelScriptCount = topLevelNewFunction.Count();
if(snapCtx->NewFunctionTopLevelScriptCount == 0)
{
snapCtx->NewFunctionTopLevelScriptArray = nullptr;
}
else
{
snapCtx->NewFunctionTopLevelScriptArray = alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(snapCtx->NewFunctionTopLevelScriptCount);
for(int32 i = 0; i < topLevelNewFunction.Count(); ++i)
{
snapCtx->NewFunctionTopLevelScriptArray[i] = topLevelNewFunction.Item(i);
}
}
snapCtx->EvalTopLevelScriptCount = topLevelEval.Count();
if(snapCtx->EvalTopLevelScriptCount == 0)
{
snapCtx->EvalTopLevelScriptArray = nullptr;
}
else
{
snapCtx->EvalTopLevelScriptArray = alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(snapCtx->EvalTopLevelScriptCount);
for(int32 i = 0; i < topLevelEval.Count(); ++i)
{
snapCtx->EvalTopLevelScriptArray[i] = topLevelEval.Item(i);
}
}
//Extract pending async modification info
const JsUtil::List<TTDPendingAsyncBufferModification, HeapAllocator>& pendingAsyncList = ctx->TTDContextInfo->GetPendingAsyncModListForSnapshot();
snapCtx->PendingAsyncModCount = pendingAsyncList.Count();
if(snapCtx->PendingAsyncModCount == 0)
{
snapCtx->PendingAsyncModArray = nullptr;
}
else
{
snapCtx->PendingAsyncModArray = alloc.SlabAllocateArray<SnapPendingAsyncBufferModification>(snapCtx->PendingAsyncModCount);
for(int32 k = 0; k < pendingAsyncList.Count(); ++k)
{
const TTDPendingAsyncBufferModification& pk = pendingAsyncList.Item(k);
snapCtx->PendingAsyncModArray[k].LogId = objToLogIdMap.Item(Js::RecyclableObject::FromVar(pk.ArrayBufferVar));
snapCtx->PendingAsyncModArray[k].Index = pk.Index;
}
}
}
void InflateScriptContext(const SnapContext* snpCtx, Js::ScriptContext* intoCtx, InflateMap* inflator,
const TTDIdentifierDictionary<uint64, TopLevelScriptLoadFunctionBodyResolveInfo*>& topLevelLoadScriptMap,
const TTDIdentifierDictionary<uint64, TopLevelNewFunctionBodyResolveInfo*>& topLevelNewScriptMap,
const TTDIdentifierDictionary<uint64, TopLevelEvalFunctionBodyResolveInfo*>& topLevelEvalScriptMap)
{
TTDAssert(wcscmp(snpCtx->ContextSRC.Contents, intoCtx->GetUrl()) == 0, "Make sure the src uri values are the same.");
intoCtx->GetLibrary()->SetIsPRNGSeeded(snpCtx->IsPNRGSeeded);
intoCtx->GetLibrary()->SetRandSeed0(snpCtx->RandomSeed0);
intoCtx->GetLibrary()->SetRandSeed1(snpCtx->RandomSeed1);
inflator->AddScriptContext(snpCtx->ScriptContextLogId, intoCtx);
intoCtx->TTDContextInfo->ClearLoadedSourcesForSnapshotRestore();
if(intoCtx->HasRecordedException())
{
intoCtx->GetAndClearRecordedException(nullptr);
}
for(uint32 i = 0; i < snpCtx->LoadedTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation& cri = snpCtx->LoadedTopLevelScriptArray[i];
Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo = topLevelLoadScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
if(fb == nullptr)
{
fb = NSSnapValues::InflateTopLevelLoadedFunctionBodyInfo(fbInfo, intoCtx);
}
else
{
intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
intoCtx->TTDContextInfo->RegisterLoadedScript(fb, cri.TopLevelBodyCtr);
intoCtx->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad_InflateReuseBody(cri.TopLevelBodyCtr, fb);
}
inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
}
//The inflation code for NewFunction and Eval uses the paths in the runtime (which assume they are in script) -- so enter here to make them happy
BEGIN_ENTER_SCRIPT(intoCtx, true, true, true)
{
for(uint32 i = 0; i < snpCtx->NewFunctionTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation& cri = snpCtx->NewFunctionTopLevelScriptArray[i];
Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
const TopLevelNewFunctionBodyResolveInfo* fbInfo = topLevelNewScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
if(fb == nullptr)
{
fb = NSSnapValues::InflateTopLevelNewFunctionBodyInfo(fbInfo, intoCtx);
}
else
{
intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
intoCtx->TTDContextInfo->RegisterNewScript(fb, cri.TopLevelBodyCtr);
intoCtx->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad_InflateReuseBody(cri.TopLevelBodyCtr, fb);
}
inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
}
for(uint32 i = 0; i < snpCtx->EvalTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation& cri = snpCtx->EvalTopLevelScriptArray[i];
Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
const TopLevelEvalFunctionBodyResolveInfo* fbInfo = topLevelEvalScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
if(fb == nullptr)
{
fb = NSSnapValues::InflateTopLevelEvalFunctionBodyInfo(fbInfo, intoCtx);
}
else
{
intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
intoCtx->TTDContextInfo->RegisterEvalScript(fb, cri.TopLevelBodyCtr);
intoCtx->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad_InflateReuseBody(cri.TopLevelBodyCtr, fb);
}
inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
}
}
END_ENTER_SCRIPT
}
void ResetPendingAsyncBufferModInfo(const SnapContext* snpCtx, Js::ScriptContext* intoCtx, InflateMap* inflator)
{
intoCtx->TTDContextInfo->ClearPendingAsyncModListForSnapRestore();
for(uint32 i = 0; i < snpCtx->PendingAsyncModCount; ++i)
{
Js::RecyclableObject* buff = intoCtx->GetThreadContext()->TTDContext->LookupObjectForLogID(snpCtx->PendingAsyncModArray[i].LogId);
uint32 index = snpCtx->PendingAsyncModArray[i].Index;
TTDAssert(Js::ArrayBuffer::Is(buff), "Not an ArrayBuffer!!!");
intoCtx->TTDContextInfo->AddToAsyncPendingList(Js::ArrayBuffer::FromVar(buff), index);
}
}
void EmitSnapContext(const SnapContext* snapCtx, FileWriter* writer, NSTokens::Separator separator)
{
writer->WriteRecordStart(separator);
writer->WriteLogTag(NSTokens::Key::ctxTag, snapCtx->ScriptContextLogId);
writer->WriteBool(NSTokens::Key::boolVal, snapCtx->IsPNRGSeeded, NSTokens::Separator::CommaSeparator);
writer->WriteUInt64(NSTokens::Key::u64Val, snapCtx->RandomSeed0, NSTokens::Separator::CommaSeparator);
writer->WriteUInt64(NSTokens::Key::u64Val, snapCtx->RandomSeed1, NSTokens::Separator::CommaSeparator);
writer->WriteString(NSTokens::Key::ctxUri, snapCtx->ContextSRC, NSTokens::Separator::CommaSeparator);
writer->WriteLengthValue(snapCtx->LoadedTopLevelScriptCount, NSTokens::Separator::CommaSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < snapCtx->LoadedTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation* cri = snapCtx->LoadedTopLevelScriptArray + i;
NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
writer->WriteRecordStart(sep);
writer->WriteUInt32(NSTokens::Key::bodyCounterId, cri->TopLevelBodyCtr);
writer->WriteAddr(NSTokens::Key::functionBodyId, cri->ContextSpecificBodyPtrId, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
writer->WriteSequenceEnd();
writer->WriteLengthValue(snapCtx->NewFunctionTopLevelScriptCount, NSTokens::Separator::CommaSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < snapCtx->NewFunctionTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation* cri = snapCtx->NewFunctionTopLevelScriptArray + i;
NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
writer->WriteRecordStart(sep);
writer->WriteUInt32(NSTokens::Key::bodyCounterId, cri->TopLevelBodyCtr);
writer->WriteAddr(NSTokens::Key::functionBodyId, cri->ContextSpecificBodyPtrId, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
writer->WriteSequenceEnd();
writer->WriteLengthValue(snapCtx->EvalTopLevelScriptCount, NSTokens::Separator::CommaSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < snapCtx->EvalTopLevelScriptCount; ++i)
{
const TopLevelFunctionInContextRelation* cri = snapCtx->EvalTopLevelScriptArray + i;
NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
writer->WriteRecordStart(sep);
writer->WriteUInt32(NSTokens::Key::bodyCounterId, cri->TopLevelBodyCtr);
writer->WriteAddr(NSTokens::Key::functionBodyId, cri->ContextSpecificBodyPtrId, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
writer->WriteSequenceEnd();
writer->WriteLengthValue(snapCtx->PendingAsyncModCount, NSTokens::Separator::CommaSeparator);
writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
for(uint32 i = 0; i < snapCtx->PendingAsyncModCount; ++i)
{
NSTokens::Separator sep = (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator;
writer->WriteRecordStart(sep);
writer->WriteLogTag(NSTokens::Key::logTag, snapCtx->PendingAsyncModArray[i].LogId);
writer->WriteUInt32(NSTokens::Key::u32Val, snapCtx->PendingAsyncModArray[i].Index, NSTokens::Separator::CommaSeparator);
writer->WriteRecordEnd();
}
writer->WriteSequenceEnd();
writer->WriteRecordEnd();
}
void ParseSnapContext(SnapContext* intoCtx, bool readSeperator, FileReader* reader, SlabAllocator& alloc)
{
reader->ReadRecordStart(readSeperator);
intoCtx->ScriptContextLogId = reader->ReadLogTag(NSTokens::Key::ctxTag);
intoCtx->IsPNRGSeeded = reader->ReadBool(NSTokens::Key::boolVal, true);
intoCtx->RandomSeed0 = reader->ReadUInt64(NSTokens::Key::u64Val, true);
intoCtx->RandomSeed1 = reader->ReadUInt64(NSTokens::Key::u64Val, true);
reader->ReadString(NSTokens::Key::ctxUri, alloc, intoCtx->ContextSRC, true);
intoCtx->LoadedTopLevelScriptCount = reader->ReadLengthValue(true);
intoCtx->LoadedTopLevelScriptArray = (intoCtx->LoadedTopLevelScriptCount != 0) ? alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(intoCtx->LoadedTopLevelScriptCount) : nullptr;
reader->ReadSequenceStart_WDefaultKey(true);
for(uint32 i = 0; i < intoCtx->LoadedTopLevelScriptCount; ++i)
{
TopLevelFunctionInContextRelation* cri = intoCtx->LoadedTopLevelScriptArray + i;
reader->ReadRecordStart(i != 0);
cri->TopLevelBodyCtr = reader->ReadUInt32(NSTokens::Key::bodyCounterId);
cri->ContextSpecificBodyPtrId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
intoCtx->NewFunctionTopLevelScriptCount = reader->ReadLengthValue(true);
intoCtx->NewFunctionTopLevelScriptArray = (intoCtx->NewFunctionTopLevelScriptCount != 0) ? alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(intoCtx->NewFunctionTopLevelScriptCount) : nullptr;
reader->ReadSequenceStart_WDefaultKey(true);
for(uint32 i = 0; i < intoCtx->NewFunctionTopLevelScriptCount; ++i)
{
TopLevelFunctionInContextRelation* cri = intoCtx->NewFunctionTopLevelScriptArray + i;
reader->ReadRecordStart(i != 0);
cri->TopLevelBodyCtr = reader->ReadUInt32(NSTokens::Key::bodyCounterId);
cri->ContextSpecificBodyPtrId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
intoCtx->EvalTopLevelScriptCount = reader->ReadLengthValue(true);
intoCtx->EvalTopLevelScriptArray = (intoCtx->EvalTopLevelScriptCount != 0) ? alloc.SlabAllocateArray<TopLevelFunctionInContextRelation>(intoCtx->EvalTopLevelScriptCount) : nullptr;
reader->ReadSequenceStart_WDefaultKey(true);
for(uint32 i = 0; i < intoCtx->EvalTopLevelScriptCount; ++i)
{
TopLevelFunctionInContextRelation* cri = intoCtx->EvalTopLevelScriptArray + i;
reader->ReadRecordStart(i != 0);
cri->TopLevelBodyCtr = reader->ReadUInt32(NSTokens::Key::bodyCounterId);
cri->ContextSpecificBodyPtrId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
intoCtx->PendingAsyncModCount = reader->ReadLengthValue(true);
intoCtx->PendingAsyncModArray = (intoCtx->PendingAsyncModCount != 0) ? alloc.SlabAllocateArray<SnapPendingAsyncBufferModification>(intoCtx->PendingAsyncModCount) : nullptr;
reader->ReadSequenceStart_WDefaultKey(true);
for(uint32 i = 0; i < intoCtx->PendingAsyncModCount; ++i)
{
reader->ReadRecordStart(i != 0);
intoCtx->PendingAsyncModArray[i].LogId = reader->ReadLogTag(NSTokens::Key::logTag);
intoCtx->PendingAsyncModArray[i].Index = reader->ReadUInt32(NSTokens::Key::u32Val, true);
reader->ReadRecordEnd();
}
reader->ReadSequenceEnd();
reader->ReadRecordEnd();
}
#if ENABLE_SNAPSHOT_COMPARE
void AssertSnapEquiv(const SnapContext* snapCtx1, const SnapContext* snapCtx2, const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, NSSnapValues::SnapRootInfoEntry*, HeapAllocator>& allRootMap1, const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, NSSnapValues::SnapRootInfoEntry*, HeapAllocator>& allRootMap2, TTDCompareMap& compareMap)
{
compareMap.DiagnosticAssert(snapCtx1->ScriptContextLogId == snapCtx2->ScriptContextLogId);
compareMap.DiagnosticAssert(snapCtx1->IsPNRGSeeded == snapCtx2->IsPNRGSeeded);
compareMap.DiagnosticAssert(snapCtx1->RandomSeed0 == snapCtx2->RandomSeed0);
compareMap.DiagnosticAssert(snapCtx1->RandomSeed1 == snapCtx2->RandomSeed1);
compareMap.DiagnosticAssert(TTStringEQForDiagnostics(snapCtx1->ContextSRC, snapCtx2->ContextSRC));
//
//TODO: For now just sanity check the number of top-level functions and let the FunctionBody matching drive any matching.
//
compareMap.DiagnosticAssert(snapCtx1->LoadedTopLevelScriptCount == snapCtx2->LoadedTopLevelScriptCount);
//TopLevelScriptLoadFunctionBodyResolveInfo* m_loadedScriptArray;
compareMap.DiagnosticAssert(snapCtx1->NewFunctionTopLevelScriptCount == snapCtx2->NewFunctionTopLevelScriptCount);
//TopLevelNewFunctionBodyResolveInfo* m_newScriptArray;
compareMap.DiagnosticAssert(snapCtx1->EvalTopLevelScriptCount == snapCtx2->EvalTopLevelScriptCount);
//TopLevelEvalFunctionBodyResolveInfo* m_evalScriptArray;
compareMap.DiagnosticAssert(snapCtx1->PendingAsyncModCount == snapCtx2->PendingAsyncModCount);
for(uint32 i = 0; i < snapCtx1->PendingAsyncModCount; ++i)
{
const SnapPendingAsyncBufferModification& pendEntry1 = snapCtx1->PendingAsyncModArray[i];
const SnapPendingAsyncBufferModification& pendEntry2 = snapCtx2->PendingAsyncModArray[i];
compareMap.DiagnosticAssert(pendEntry1.LogId == pendEntry2.LogId && pendEntry1.Index == pendEntry2.Index);
compareMap.H1PendingAsyncModBufferSet.AddNew(allRootMap1.Item(pendEntry1.LogId)->LogObject);
compareMap.H2PendingAsyncModBufferSet.AddNew(allRootMap2.Item(pendEntry2.LogId)->LogObject);
}
}
#endif
}
}
#endif