//------------------------------------------------------------------------------------------------------- // 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(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(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(NSTokens::Key::ttdVarTag, TTDVarEmitTag::TTDVarDouble); writer->WriteDouble(NSTokens::Key::doubleVal, Js::JavascriptNumber::GetValue(var), NSTokens::Separator::CommaSeparator); } #endif } else { writer->WriteTag(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 readSeparator, FileReader* reader) { reader->ReadRecordStart(readSeparator); TTDVar res = nullptr; TTDVarEmitTag tag = reader->ReadTag(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& 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(); 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 readSeparator, FileReader* reader, SlabAllocator& alloc, const TTDIdentifierDictionary& ptrIdToTypeMap) { reader->ReadRecordStart(readSeparator); 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(); 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 ////////////////// Field(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(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 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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(slotInfo->SlotCount); slotInfo->PIDArray = alloc.SlabAllocateArray(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: { Field(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(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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(funcScopeInfo->ScopeCount); for(uint32 i = 0; i < funcScopeInfo->ScopeCount; ++i) { reader->ReadRecordStart(i != 0); funcScopeInfo->ScopeArray[i].Tag = reader->ReadTag(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(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(capabilityInfo->CapabilityId, res); } return inflator->LookupInflatedPromiseInfo(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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(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(reactionInfo->PromiseReactionId, res); } return inflator->LookupInflatedPromiseInfo(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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(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(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(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 readSeparator, bool parseInline, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(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(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(NSTokens::Key::loadFlag, fbInfo->LoadFlag, NSTokens::Separator::CommaSeparator); writer->WriteRecordEnd(); } void ParseTopLevelLoadedFunctionBodyInfo(TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc) { NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeparator, false, threadContext, reader, alloc); fbInfo->LoadFlag = reader->ReadTag(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 readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc) { NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeparator, 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 readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc) { NSSnapValues::ParseTopLevelCommonBodyResolveInfo(&fbInfo->TopLevelBase, readSeparator, 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& 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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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& objToLogIdMap, const JsUtil::BaseHashSet& liveTopLevelBodies, 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 topLevelScriptLoad(&HeapAllocator::Instance); JsUtil::List topLevelNewFunction(&HeapAllocator::Instance); JsUtil::List topLevelEval(&HeapAllocator::Instance); ctx->TTDContextInfo->GetLoadedSources(&liveTopLevelBodies, topLevelScriptLoad, topLevelNewFunction, topLevelEval); snapCtx->LoadedTopLevelScriptCount = topLevelScriptLoad.Count(); if(snapCtx->LoadedTopLevelScriptCount == 0) { snapCtx->LoadedTopLevelScriptArray = nullptr; } else { snapCtx->LoadedTopLevelScriptArray = alloc.SlabAllocateArray(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(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(snapCtx->EvalTopLevelScriptCount); for(int32 i = 0; i < topLevelEval.Count(); ++i) { snapCtx->EvalTopLevelScriptArray[i] = topLevelEval.Item(i); } } //Extract pending async modification info const JsUtil::List& pendingAsyncList = ctx->TTDContextInfo->GetPendingAsyncModListForSnapshot(); snapCtx->PendingAsyncModCount = pendingAsyncList.Count(); if(snapCtx->PendingAsyncModCount == 0) { snapCtx->PendingAsyncModArray = nullptr; } else { snapCtx->PendingAsyncModArray = alloc.SlabAllocateArray(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& topLevelLoadScriptMap, const TTDIdentifierDictionary& topLevelNewScriptMap, const TTDIdentifierDictionary& 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 readSeparator, FileReader* reader, SlabAllocator& alloc) { reader->ReadRecordStart(readSeparator); 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(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(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(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(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& allRootMap1, const JsUtil::BaseDictionary& 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