//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once #if ENABLE_TTD namespace TTD { class TTDTimer { private: PlatformAgnostic::DateTime::HiResTimer m_timer; public: TTDTimer() : m_timer() { ; } ~TTDTimer() { ; } double Now() { return this->m_timer.Now(); } }; namespace JsSupport { //return true if the Var is a tagged number (inline) bool IsVarTaggedInline(Js::Var v); //return true if the Var is a ptr var value bool IsVarPtrValued(Js::Var v); //return true if the Var is a primitive value (string, number, symbol, etc.) bool IsVarPrimitiveKind(Js::Var v); //return true if the Var is a richer value (enumerator, dynamicObject, array, etc.) bool IsVarComplexKind(Js::Var v); #if ENABLE_TTD_INTERNAL_DIAGNOSTICS //Check if 2 inline values are equivalent -- JIT can use number vs. Int in interp mode but as long as values mean the same thing we are cool bool AreInlineVarsEquiv(Js::Var v1, Js::Var v2); #endif //Ensure a function is fully parsed/deserialized Js::FunctionBody* ForceAndGetFunctionBody(Js::ParseableFunctionInfo* pfi); void WriteCodeToFile(ThreadContext* threadContext, bool fromEvent, uint32 bodyId, bool isUtf8Source, byte* sourceBuffer, uint32 length); void ReadCodeFromFile(ThreadContext* threadContext, bool fromEvent, uint32 bodyId, bool isUtf8Source, byte* sourceBuffer, uint32 length); } namespace NSSnapValues { ////////////////// //Helpers for TTDVars enum class TTDVarEmitTag { TTDVarInvalid = 0x0, TTDVarNull, TTDVarInt, TTDVarDouble, TTDVarAddr }; //serialize the TTDVar void EmitTTDVar(TTDVar var, FileWriter* writer, NSTokens::Separator separator); //de-serialize the TTDVar TTDVar ParseTTDVar(bool readSeparator, FileReader* reader); #if ENABLE_SNAPSHOT_COMPARE bool CheckSnapEquivTTDDouble(double d1, double d2); void AssertSnapEquivTTDVar_Helper(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, TTDComparePath::StepKind stepKind, const TTDComparePath::PathEntry& next); void AssertSnapEquivTTDVar_Property(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid); void AssertSnapEquivTTDVar_PropertyGetter(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid); void AssertSnapEquivTTDVar_PropertySetter(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, Js::PropertyId pid); void AssertSnapEquivTTDVar_Array(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, uint32 index); void AssertSnapEquivTTDVar_SlotArray(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, uint32 index); void AssertSnapEquivTTDVar_Special(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, const char16* specialField); void AssertSnapEquivTTDVar_SpecialArray(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, const char16* specialField, uint32 index); #endif ////////////////// //A structure for the primitive javascript types struct SnapPrimitiveValue { //The id (address of the object) TTD_PTR_ID PrimitiveValueId; //The type of the primitive value NSSnapType::SnapType* SnapType; //The optional well known token for this object (or INVALID) TTD_WELLKNOWN_TOKEN OptWellKnownToken; union { BOOL u_boolValue; int64 u_int64Value; uint64 u_uint64Value; double u_doubleValue; TTString* u_stringValue; Js::PropertyId u_propertyIdValue; }; }; void ExtractSnapPrimitiveValue(SnapPrimitiveValue* snapValue, Js::RecyclableObject* jsValue, bool isWellKnown, const TTDIdentifierDictionary& idToTypeMap, SlabAllocator& alloc); void InflateSnapPrimitiveValue(const SnapPrimitiveValue* snapValue, InflateMap* inflator); void EmitSnapPrimitiveValue(const SnapPrimitiveValue* snapValue, FileWriter* writer, NSTokens::Separator separator); void ParseSnapPrimitiveValue(SnapPrimitiveValue* snapValue, bool readSeparator, FileReader* reader, SlabAllocator& alloc, const TTDIdentifierDictionary& ptrIdToTypeMap); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SnapPrimitiveValue* v1, const SnapPrimitiveValue* v2, TTDCompareMap& compareMap); #endif ////////////////// //If a scope is a slot array this class encodes the slot information and original address of the slot array struct SlotArrayInfo { //The unique id for the slot array scope entry TTD_PTR_ID SlotId; //The tag of the script context that this slot array is associated with TTD_LOG_PTR_ID ScriptContextLogId; //The number of slots in the scope array uint32 SlotCount; //The data values for the slots in the scope entry TTDVar* Slots; //The property ids associated with each index Js::PropertyId* PIDArray; //The meta-data for the slot array bool isFunctionBodyMetaData; TTD_PTR_ID OptFunctionBodyId; TTD_PTR_ID OptDebugScopeId; TTD_WELLKNOWN_TOKEN OptWellKnownDbgScope; }; Field(Js::Var)* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator); void EmitSlotArrayInfo(const SlotArrayInfo* slotInfo, FileWriter* writer, NSTokens::Separator separator); void ParseSlotArrayInfo(SlotArrayInfo* slotInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SlotArrayInfo* sai1, const SlotArrayInfo* sai2, TTDCompareMap& compareMap); #endif //A struct to represent a single scope entry for a function struct ScopeInfoEntry { //The tag indicating what type of scope entry this is Js::ScopeType Tag; //The id for looking up the entry data //Either ptr object for Activation and With Scopes or the slotid for SlotArray Scopes TTD_PTR_ID IDValue; }; //A class to represent a scope list for a script function struct ScriptFunctionScopeInfo { //The unique id for the scope array associated with this function TTD_PTR_ID ScopeId; //The id of the script context that this slot array is associated with TTD_LOG_PTR_ID ScriptContextLogId; //The number of scope entries uint16 ScopeCount; //The array of scope entries this function has ScopeInfoEntry* ScopeArray; }; Js::FrameDisplay* InflateScriptFunctionScopeInfo(const ScriptFunctionScopeInfo* funcScopeInfo, InflateMap* inflator); void EmitScriptFunctionScopeInfo(const ScriptFunctionScopeInfo* funcScopeInfo, FileWriter* writer, NSTokens::Separator separator); void ParseScriptFunctionScopeInfo(ScriptFunctionScopeInfo* funcScopeInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const ScriptFunctionScopeInfo* funcScopeInfo1, const ScriptFunctionScopeInfo* funcScopeInfo2, TTDCompareMap& compareMap); #endif ////////////////// //Capability info for a promise struct SnapPromiseCapabilityInfo { //The unique id for the capability entry TTD_PTR_ID CapabilityId; TTDVar PromiseVar; TTDVar ResolveVar; TTDVar RejectVar; }; Js::JavascriptPromiseCapability* InflatePromiseCapabilityInfo(const SnapPromiseCapabilityInfo* capabilityInfo, Js::ScriptContext* ctx, InflateMap* inflator); void EmitPromiseCapabilityInfo(const SnapPromiseCapabilityInfo* capabilityInfo, FileWriter* writer, NSTokens::Separator separator); void ParsePromiseCapabilityInfo(SnapPromiseCapabilityInfo* capabilityInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SnapPromiseCapabilityInfo* capabilityInfo1, const SnapPromiseCapabilityInfo* capabilityInfo2, TTDCompareMap& compareMap); #endif //A struct to represent a PromiseReaction struct SnapPromiseReactionInfo { //The unique id for PromiseReaction TTD_PTR_ID PromiseReactionId; TTD_PTR_ID HandlerObjId; SnapPromiseCapabilityInfo Capabilities; }; Js::JavascriptPromiseReaction* InflatePromiseReactionInfo(const SnapPromiseReactionInfo* reactionInfo, Js::ScriptContext* ctx, InflateMap* inflator); void EmitPromiseReactionInfo(const SnapPromiseReactionInfo* reactionInfo, FileWriter* writer, NSTokens::Separator separator); void ParsePromiseReactionInfo(SnapPromiseReactionInfo* reactionInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SnapPromiseReactionInfo* reactionInfo1, const SnapPromiseReactionInfo* reactionInfo2, TTDCompareMap& compareMap); #endif ////////////////// //Information on the scopechain for a function body struct SnapFunctionBodyScopeChain { //The number of scopes associated with this function body uint32 ScopeCount; //The Ids of the scopes TTD_PTR_ID* ScopeArray; }; void ExtractSnapFunctionBodyScopeChain(bool isWellKnownFunction, SnapFunctionBodyScopeChain& scopeChain, Js::FunctionBody* fb, SlabAllocator& alloc); void EmitSnapFunctionBodyScopeChain(const SnapFunctionBodyScopeChain& scopeChain, FileWriter* writer); void ParseSnapFunctionBodyScopeChain(SnapFunctionBodyScopeChain& scopeChain, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SnapFunctionBodyScopeChain& chain1, const SnapFunctionBodyScopeChain& chain2, TTDCompareMap& compareMap); #endif ////////////////// //Information that is common to all top-level bodies struct TopLevelCommonBodyResolveInfo { //The context this body is associated with TTD_LOG_PTR_ID ScriptContextLogId; //A unique counter we produce for every top-level body as it is loaded uint32 TopLevelBodyCtr; //The string name of the function TTString FunctionName; //The module id and source context Js::ModuleID ModuleId; uint64 SourceContextId; //Src URI may be null TTString SourceUri; //The source length/buffer and if it is utf8 or char16 encoded bool IsUtf8; uint32 ByteLength; byte* SourceBuffer; //The (possibly empty) scope chain info SnapFunctionBodyScopeChain ScopeChainInfo; //The number of bytes (or -1 if not set) and the buffer for the serialized bytecode mutable DWORD DbgSerializedBytecodeSize; mutable byte* DbgSerializedBytecodeBuffer; }; //Extract WITHOUT COPYING the info needed for this top level function -- use in script context when function is parsed to keep all the info together and then we do the copying later when doing snapshots void ExtractTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, uint64 sourceContextId, bool isUtf8source, const byte* source, uint32 sourceLen, SlabAllocator& alloc); void EmitTopLevelCommonBodyResolveInfo(const TopLevelCommonBodyResolveInfo* fbInfo, bool emitInline, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator); void ParseTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, bool readSeparator, bool parseInline, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const TopLevelCommonBodyResolveInfo* fbInfo1, const TopLevelCommonBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap); #endif //A struct that we can use to resolve a top-level script load function bodies and info struct TopLevelScriptLoadFunctionBodyResolveInfo { //The base information for the top-level resolve info TopLevelCommonBodyResolveInfo TopLevelBase; //The script flag this was loaded with LoadScriptFlag LoadFlag; }; 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); Js::FunctionBody* InflateTopLevelLoadedFunctionBodyInfo(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx); void EmitTopLevelLoadedFunctionBodyInfo(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator); void ParseTopLevelLoadedFunctionBodyInfo(TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo1, const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap); #endif //A struct that we can use to resolve a top-level 'new Function' function bodies and info struct TopLevelNewFunctionBodyResolveInfo { //The base information for the top-level resolve info TopLevelCommonBodyResolveInfo TopLevelBase; }; void ExtractTopLevelNewFunctionBodyInfo(TopLevelNewFunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint32 topLevelCtr, Js::ModuleID moduleId, const char16* source, uint32 sourceLen, SlabAllocator& alloc); Js::FunctionBody* InflateTopLevelNewFunctionBodyInfo(const TopLevelNewFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx); void EmitTopLevelNewFunctionBodyInfo(const TopLevelNewFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator); void ParseTopLevelNewFunctionBodyInfo(TopLevelNewFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const TopLevelNewFunctionBodyResolveInfo* fbInfo1, const TopLevelNewFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap); #endif //A struct that we can use to resolve a top-level 'eval' function bodies and info struct TopLevelEvalFunctionBodyResolveInfo { //The base information for the top-level resolve info TopLevelCommonBodyResolveInfo TopLevelBase; //Additional data for handling the eval uint64 EvalFlags; bool RegisterDocument; bool IsIndirect; bool IsStrictMode; }; 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); Js::FunctionBody* InflateTopLevelEvalFunctionBodyInfo(const TopLevelEvalFunctionBodyResolveInfo* fbInfo, Js::ScriptContext* ctx); void EmitTopLevelEvalFunctionBodyInfo(const TopLevelEvalFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator); void ParseTopLevelEvalFunctionBodyInfo(TopLevelEvalFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const TopLevelEvalFunctionBodyResolveInfo* fbInfo1, const TopLevelEvalFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap); #endif //A struct that we can use to resolve a function body during inflation struct FunctionBodyResolveInfo { //The id this body is associated with TTD_PTR_ID FunctionBodyId; //The context this body is associated with TTD_LOG_PTR_ID ScriptContextLogId; //The string name of the function TTString FunctionName; //The known path to a function with the desired body TTD_WELLKNOWN_TOKEN OptKnownPath; //The ptr id of the parent function body TTD_PTR_ID OptParentBodyId; //The line number the function is def starts on int64 OptLine; //The column number the function is def starts on int64 OptColumn; //The (possibly empty) scope chain info SnapFunctionBodyScopeChain ScopeChainInfo; }; void ExtractFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, bool isWellKnown, SlabAllocator& alloc); void InflateFunctionBody(const FunctionBodyResolveInfo* fbInfo, InflateMap* inflator, const TTDIdentifierDictionary& idToFbResolveMap); void EmitFunctionBodyInfo(const FunctionBodyResolveInfo* fbInfo, FileWriter* writer, NSTokens::Separator separator); void ParseFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const FunctionBodyResolveInfo* fbInfo1, const FunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap); #endif ////////////////// struct SnapRootInfoEntry { //The log id value TTD_LOG_PTR_ID LogId; //The object that this log id is mapped to TTD_PTR_ID LogObject; //True if the root maybe long lived/false otherwise bool MaybeLongLivedRoot; }; struct SnapPendingAsyncBufferModification { //The log id value TTD_LOG_PTR_ID LogId; //The index value associated with this modification uint32 Index; }; //A structure for serializing/tracking script contexts struct SnapContext { //The tag id of the script context (actually the global object associated with this context) TTD_LOG_PTR_ID ScriptContextLogId; //The random seed for the context bool IsPNRGSeeded; uint64 RandomSeed0; uint64 RandomSeed1; //The main URI of the context TTString ContextSRC; //A list of all *root* scripts that have been loaded into this context uint32 LoadedTopLevelScriptCount; TopLevelFunctionInContextRelation* LoadedTopLevelScriptArray; uint32 NewFunctionTopLevelScriptCount; TopLevelFunctionInContextRelation* NewFunctionTopLevelScriptArray; uint32 EvalTopLevelScriptCount; TopLevelFunctionInContextRelation* EvalTopLevelScriptArray; //A list of all the pending async buffer modifications uint32 PendingAsyncModCount; SnapPendingAsyncBufferModification* PendingAsyncModArray; }; void ExtractScriptContext(SnapContext* snapCtx, Js::ScriptContext* ctx, const JsUtil::BaseDictionary& objToLogIdMap, const JsUtil::BaseHashSet& liveTopLevelBodies, SlabAllocator& alloc); void InflateScriptContext(const SnapContext* snpCtx, Js::ScriptContext* intoCtx, InflateMap* inflator, const TTDIdentifierDictionary& topLevelLoadScriptMap, const TTDIdentifierDictionary& topLevelNewScriptMap, const TTDIdentifierDictionary& topLevelEvalScriptMap); void ResetPendingAsyncBufferModInfo(const SnapContext* snpCtx, Js::ScriptContext* intoCtx, InflateMap* inflator); void EmitSnapContext(const SnapContext* snapCtx, FileWriter* writer, NSTokens::Separator separator); void ParseSnapContext(SnapContext* intoCtx, bool readSeparator, FileReader* reader, SlabAllocator& alloc); #if ENABLE_SNAPSHOT_COMPARE void AssertSnapEquiv(const SnapContext* snapCtx1, const SnapContext* snapCtx2, const JsUtil::BaseDictionary& allRootMap1, const JsUtil::BaseDictionary& allRootMap2, TTDCompareMap& compareMap); #endif } } #endif