示例#1
0
CHAKRA_API JsDiagRequestAsyncBreak(
    _In_ JsRuntimeHandle runtimeHandle)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
    return JsErrorCategoryUsage;
#else
    return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {

        VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);

        JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);

        JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();

        VALIDATE_IS_DEBUGGING(jsrtDebugManager);

        for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
        scriptContext != nullptr && !scriptContext->IsClosed();
            scriptContext = scriptContext->next)
        {
            jsrtDebugManager->EnableAsyncBreak(scriptContext);
        }

        return JsNoError;
    });
#endif
}
示例#2
0
CHAKRA_API
JsModuleEvaluation(
    _In_ JsModuleRecord requestModule,
    _Outptr_result_maybenull_ JsValueRef* result)
{
    if (!Js::SourceTextModuleRecord::Is(requestModule))
    {
        return JsErrorInvalidArgument;
    }
    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
    if (moduleRecord->WasEvaluated())
    {
        return JsErrorModuleEvaluated;
    }
    if (result != nullptr)
    {
        *result = JS_INVALID_REFERENCE;
    }
    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
        SmartFPUControl smartFpuControl;
        if (smartFpuControl.HasErr())
        {
            return JsErrorBadFPUState;
        }
        JsValueRef returnRef = moduleRecord->ModuleEvaluation();
        if (result != nullptr)
        {
            *result = returnRef;
        }
        return JsNoError;
    });
    return errorCode;
}
示例#3
0
CHAKRA_API JsDiagSetBreakpoint(
    _In_ unsigned int scriptId,
    _In_ unsigned int lineNumber,
    _In_ unsigned int columnNumber,
    _Out_ JsValueRef *breakpoint)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
    return JsErrorCategoryUsage;
#else
    return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext* scriptContext) -> JsErrorCode {
        PARAM_NOT_NULL(breakpoint);
        *breakpoint = JS_INVALID_REFERENCE;

        JsrtContext* currentContext = JsrtContext::GetCurrent();
        JsrtRuntime* runtime = currentContext->GetRuntime();

        ThreadContextScope scope(runtime->GetThreadContext());

        if (!scope.IsValid())
        {
            return JsErrorWrongThread;
        }

        VALIDATE_IS_DEBUGGING(runtime->GetJsrtDebugManager());

        Js::Utf8SourceInfo* utf8SourceInfo = nullptr;

        for (Js::ScriptContext* currentScriptContext = runtime->GetThreadContext()->GetScriptContextList();
            currentScriptContext != nullptr && utf8SourceInfo == nullptr && !currentScriptContext->IsClosed();
            currentScriptContext = currentScriptContext->next)
        {
            currentScriptContext->MapScript([&](Js::Utf8SourceInfo* sourceInfo) -> bool
            {
                if (sourceInfo->GetSourceInfoId() == scriptId)
                {
                    utf8SourceInfo = sourceInfo;
                    return true;
                }
                return false;
            });
        }

        if (utf8SourceInfo != nullptr && utf8SourceInfo->HasDebugDocument())
        {
            JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
            Js::DynamicObject* bpObject = jsrtDebugManager->SetBreakPoint(currentContext->GetScriptContext(), utf8SourceInfo, lineNumber, columnNumber);

            if(bpObject != nullptr)
            {
                *breakpoint = bpObject;
                return JsNoError;
            }

            return JsErrorDiagUnableToPerformAction;
        }

        return JsErrorDiagObjectNotFound;
    });
#endif
}
示例#4
0
CHAKRA_API JsDiagStopDebugging(
    _In_ JsRuntimeHandle runtimeHandle,
    _Out_opt_ void** callbackState)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
    return JsErrorCategoryUsage;
#else
    return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {

        VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);

        if (callbackState != nullptr)
        {
            *callbackState = nullptr;
        }

        JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
        ThreadContext * threadContext = runtime->GetThreadContext();

        VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext);

        JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();

        VALIDATE_IS_DEBUGGING(jsrtDebugManager);

        for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
        scriptContext != nullptr && !scriptContext->IsClosed();
            scriptContext = scriptContext->next)
        {
            Assert(scriptContext->IsScriptContextInDebugMode());

            HRESULT hr;
            if (FAILED(hr = scriptContext->OnDebuggerDetached()))
            {
                Debugger_AttachDetach_unrecoverable_error(hr); // Inconsistent state, we can't continue from here
                return JsErrorFatal;
            }

            Js::DebugContext* debugContext = scriptContext->GetDebugContext();

            Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer();
            probeContainer->UninstallInlineBreakpointProbe(nullptr);
            probeContainer->UninstallDebuggerScriptOptionCallback();

            jsrtDebugManager->ClearBreakpointDebugDocumentDictionary();
        }

        void* cbState = jsrtDebugManager->GetAndClearCallbackState();

        if (callbackState != nullptr)
        {
            *callbackState = cbState;
        }

        return JsNoError;
    });
#endif
}
示例#5
0
CHAKRA_API
JsParseModuleSource(
    _In_ JsModuleRecord requestModule,
    _In_ JsSourceContext sourceContext,
    _In_ byte* sourceText,
    _In_ unsigned int sourceLength,
    _In_ JsParseModuleSourceFlags sourceFlag,
    _Outptr_result_maybenull_ JsValueRef* exceptionValueRef)
{
    PARAM_NOT_NULL(requestModule);
    PARAM_NOT_NULL(exceptionValueRef);
    if (sourceFlag > JsParseModuleSourceFlags_DataIsUTF8)
    {
        return JsErrorInvalidArgument;
    }

    *exceptionValueRef = JS_INVALID_REFERENCE;
    HRESULT hr;
    if (!Js::SourceTextModuleRecord::Is(requestModule))
    {
        return JsErrorInvalidArgument;
    }
    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
    if (moduleRecord->WasParsed())
    {
        return JsErrorModuleParsed;
    }
    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
    JsErrorCode errorCode = GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
        SourceContextInfo* sourceContextInfo = scriptContext->GetSourceContextInfo(sourceContext, nullptr);
        if (sourceContextInfo == nullptr)
        {
            sourceContextInfo = scriptContext->CreateSourceContextInfo(sourceContext, nullptr, 0, nullptr, nullptr, 0);
        }
        SRCINFO si = {
            /* sourceContextInfo   */ sourceContextInfo,
            /* dlnHost             */ 0,
            /* ulColumnHost        */ 0,
            /* lnMinHost           */ 0,
            /* ichMinHost          */ 0,
            /* ichLimHost          */ static_cast<ULONG>(sourceLength),
            /* ulCharOffset        */ 0,
            /* mod                 */ 0,
            /* grfsi               */ 0
        };
        hr = moduleRecord->ParseSource(sourceText, sourceLength, &si, exceptionValueRef, sourceFlag == JsParseModuleSourceFlags_DataIsUTF8 ? true : false);
        if (FAILED(hr))
        {
            return JsErrorScriptCompile;
        }
        return JsNoError;
    });
    return errorCode;
}
    void RuntimeContextInfo::LoadAndOrderPropertyNames(Js::RecyclableObject* obj, JsUtil::List<const Js::PropertyRecord*, HeapAllocator>& propertyList)
    {
        TTDAssert(propertyList.Count() == 0, "This should be empty.");

        Js::ScriptContext* ctx = obj->GetScriptContext();
        uint32 propcount = (uint32)obj->GetPropertyCount();

        //get all of the properties
        for(uint32 i = 0; i < propcount; ++i)
        {
            Js::PropertyIndex propertyIndex = (Js::PropertyIndex)i;
            Js::PropertyId propertyId = obj->GetPropertyId(propertyIndex);

            if((propertyId != Js::Constants::NoProperty) & (!Js::IsInternalPropertyId(propertyId)))
            {
                TTDAssert(obj->HasOwnProperty(propertyId), "We are assuming this is own property count.");

                propertyList.Add(ctx->GetPropertyName(propertyId));
            }
        }

        //now sort the list so the traversal order is stable
        //Rock a custom shell sort!!!!
        const int32 gaps[6] = { 132, 57, 23, 10, 4, 1 };

        int32 llen = propertyList.Count();
        for(uint32 gapi = 0; gapi < 6; ++gapi)
        {
            int32 gap = gaps[gapi];

            for(int32 i = gap; i < llen; i++)
            {
                const Js::PropertyRecord* temp = propertyList.Item(i);

                int32 j = 0;
                for(j = i; j >= gap && PropertyNameCmp(propertyList.Item(j - gap), temp); j -= gap)
                {
                    const Js::PropertyRecord* shiftElem = propertyList.Item(j - gap);
                    propertyList.SetItem(j, shiftElem);
                }

                propertyList.SetItem(j, temp);
            }
        }
    }
示例#7
0
CHAKRA_API
JsGetModuleHostInfo(
    _In_  JsModuleRecord requestModule,
    _In_ JsModuleHostInfoKind moduleHostInfo,
    _Outptr_result_maybenull_ void** hostInfo)
{
    if (!Js::SourceTextModuleRecord::Is(requestModule) || (hostInfo == nullptr))
    {
        return JsErrorInvalidArgument;
    }
    *hostInfo = nullptr;
    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
        JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
        switch (moduleHostInfo)
        {
        case JsModuleHostInfo_Exception:
            if (moduleRecord->GetErrorObject() != nullptr)
            {
                *hostInfo = moduleRecord->GetErrorObject();
            }
            break;
        case JsModuleHostInfo_HostDefined:
            *hostInfo = moduleRecord->GetHostDefined();
            break;
        case JsModuleHostInfo_FetchImportedModuleCallback:
            *hostInfo = reinterpret_cast<void*>(currentContext->GetHostScriptContext()->GetFetchImportedModuleCallback());
            break;
        case JsModuleHostInfo_FetchImportedModuleFromScriptCallback:
            *hostInfo = reinterpret_cast<void*>(currentContext->GetHostScriptContext()->GetFetchImportedModuleFromScriptCallback());
            break;
        case JsModuleHostInfo_NotifyModuleReadyCallback:
            *hostInfo = reinterpret_cast<void*>(currentContext->GetHostScriptContext()->GetNotifyModuleReadyCallback());
            break;
        default:
            return JsInvalidModuleHostInfoKind;
        };
        return JsNoError;
    });
    return errorCode;
}
示例#8
0
CHAKRA_API JsDiagGetBreakpoints(
    _Out_ JsValueRef *breakpoints)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
    return JsErrorCategoryUsage;
#else
    return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {

        PARAM_NOT_NULL(breakpoints);

        *breakpoints = JS_INVALID_REFERENCE;

        JsrtContext *currentContext = JsrtContext::GetCurrent();

        Js::JavascriptArray* bpsArray = currentContext->GetScriptContext()->GetLibrary()->CreateArray();

        JsrtRuntime * runtime = currentContext->GetRuntime();

        ThreadContextScope scope(runtime->GetThreadContext());

        if (!scope.IsValid())
        {
            return JsErrorWrongThread;
        }

        JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();

        VALIDATE_IS_DEBUGGING(jsrtDebugManager);

        for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
        scriptContext != nullptr && !scriptContext->IsClosed();
            scriptContext = scriptContext->next)
        {
            jsrtDebugManager->GetBreakpoints(&bpsArray, scriptContext);
        }

        *breakpoints = bpsArray;

        return JsNoError;
    });
#endif
}
示例#9
0
CHAKRA_API
JsSetModuleHostInfo(
    _In_ JsModuleRecord requestModule,
    _In_ JsModuleHostInfoKind moduleHostInfo,
    _In_ void* hostInfo)
{
    if (!Js::SourceTextModuleRecord::Is(requestModule))
    {
        return JsErrorInvalidArgument;
    }
    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
        JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
        switch (moduleHostInfo)
        {
        case JsModuleHostInfo_Exception:
            moduleRecord->OnHostException(hostInfo);
            break;
        case JsModuleHostInfo_HostDefined:
            moduleRecord->SetHostDefined(hostInfo);
            break;
        case JsModuleHostInfo_FetchImportedModuleCallback:
            currentContext->GetHostScriptContext()->SetFetchImportedModuleCallback(reinterpret_cast<FetchImportedModuleCallBack>(hostInfo));
            break;
        case JsModuleHostInfo_FetchImportedModuleFromScriptCallback:
            currentContext->GetHostScriptContext()->SetFetchImportedModuleFromScriptCallback(reinterpret_cast<FetchImportedModuleFromScriptCallBack>(hostInfo));
            break;
        case JsModuleHostInfo_NotifyModuleReadyCallback:
            currentContext->GetHostScriptContext()->SetNotifyModuleReadyCallback(reinterpret_cast<NotifyModuleReadyCallback>(hostInfo));
            break;
        default:
            return JsInvalidModuleHostInfoKind;
        };
        return JsNoError;
    });
    return errorCode;
}
示例#10
0
CHAKRA_API JsDiagStartDebugging(
    _In_ JsRuntimeHandle runtimeHandle,
    _In_ JsDiagDebugEventCallback debugEventCallback,
    _In_opt_ void* callbackState)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
    return JsErrorCategoryUsage;
#else
    return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {

        VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);

        PARAM_NOT_NULL(debugEventCallback);

        JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
        ThreadContext * threadContext = runtime->GetThreadContext();

        VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext);

        if (runtime->GetJsrtDebugManager() != nullptr && runtime->GetJsrtDebugManager()->IsDebugEventCallbackSet())
        {
            return JsErrorDiagAlreadyInDebugMode;
        }

        // Create the debug object to save callback function and data
        runtime->EnsureJsrtDebugManager();

        JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();

        jsrtDebugManager->SetDebugEventCallback(debugEventCallback, callbackState);

        if (threadContext->GetDebugManager() != nullptr)
        {
            threadContext->GetDebugManager()->SetLocalsDisplayFlags(Js::DebugManager::LocalsDisplayFlags::LocalsDisplayFlags_NoGroupMethods);
        }

        for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
        scriptContext != nullptr && !scriptContext->IsClosed();
            scriptContext = scriptContext->next)
        {
            Assert(!scriptContext->IsScriptContextInDebugMode());

            Js::DebugContext* debugContext = scriptContext->GetDebugContext();

            if (debugContext->GetHostDebugContext() == nullptr)
            {
                debugContext->SetHostDebugContext(jsrtDebugManager);
            }

            HRESULT hr;
            if (FAILED(hr = scriptContext->OnDebuggerAttached()))
            {
                Debugger_AttachDetach_fatal_error(hr); // Inconsistent state, we can't continue from here
                return JsErrorFatal;
            }

            // ScriptContext might get closed in OnDebuggerAttached
            if (!scriptContext->IsClosed())
            {
                Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer();
                probeContainer->InitializeInlineBreakEngine(jsrtDebugManager);
                probeContainer->InitializeDebuggerScriptOptionCallback(jsrtDebugManager);
            }
        }

        return JsNoError;
    });
#endif
}
示例#11
0
void
Sym::Dump(IRDumpFlags flags, const ValueType valueType)
{
    bool const AsmDumpMode = flags & IRDumpFlags_AsmDumpMode;
    bool const SimpleForm = !!(flags & IRDumpFlags_SimpleForm);

    if (AsmDumpMode)
    {
        if (this->IsStackSym() && this->AsStackSym()->IsArgSlotSym())
        {
            Output::Print(L"arg ");
        }
        else if (this->IsStackSym() && this->AsStackSym()->IsParamSlotSym())
        {
            Output::Print(L"param ");
        }
    }
    else
    {
        if (this->IsStackSym() && this->AsStackSym()->IsArgSlotSym())
        {
            if (this->AsStackSym()->m_isInlinedArgSlot)
            {
                Output::Print(L"iarg%d", this->AsStackSym()->GetArgSlotNum());
            }
            else
            {
                Output::Print(L"arg%d", this->AsStackSym()->GetArgSlotNum());
            }
            Output::Print(L"(s%d)", m_id);
        }
        else if (this->IsStackSym() && this->AsStackSym()->IsParamSlotSym())
        {
            Output::Print(L"prm%d", this->AsStackSym()->GetParamSlotNum());
        }
        else
        {
            if (!this->IsPropertySym() || !SimpleForm)
            {
                Output::Print(L"s%d", m_id);
            }
            if (this->IsStackSym())
            {
                if(Js::Configuration::Global.flags.Debug && this->AsStackSym()->HasByteCodeRegSlot())
                {
                    StackSym* sym =  this->AsStackSym();
                    Js::FunctionBody* functionBody = sym->GetByteCodeFunc()->GetJnFunction();
                    if(functionBody->GetPropertyIdOnRegSlotsContainer())
                    {
                        if(functionBody->IsNonTempLocalVar(sym->GetByteCodeRegSlot()))
                        {
                            uint index = sym->GetByteCodeRegSlot() - functionBody->GetConstantCount();
                            Js::PropertyId propertyId = functionBody->GetPropertyIdOnRegSlotsContainer()->propertyIdsForRegSlots[index];
                            Output::Print(L"(%s)", functionBody->GetScriptContext()->GetPropertyNameLocked(propertyId)->GetBuffer());
                        }
                    }
                }
                if (this->AsStackSym()->IsVar())
                {
                    if (this->AsStackSym()->HasObjectTypeSym() && !SimpleForm)
                    {
                        Output::Print(L"<s%d>", this->AsStackSym()->GetObjectTypeSym()->m_id);
                    }
                }
                else
                {
                    StackSym *varSym = this->AsStackSym()->GetVarEquivSym(nullptr);
                    if (varSym)
                    {
                        Output::Print(L"(s%d)", varSym->m_id);
                    }
                }
                if (!SimpleForm)
                {
                    if (this->AsStackSym()->m_builtInIndex != Js::BuiltinFunction::None)
                    {
                        Output::Print(L"[ffunc]");
                    }
                }
            }
        }
        if(IsStackSym())
        {
            IR::Opnd::DumpValueType(valueType);
        }
    }

    if (this->IsPropertySym())
    {
        PropertySym *propertySym = this->AsPropertySym();

        if (!SimpleForm)
        {
            Output::Print(L"(");
        }

        Js::ScriptContext* scriptContext;
        switch (propertySym->m_fieldKind)
        {
        case PropertyKindData:
        {
            propertySym->m_stackSym->Dump(flags, valueType);
            scriptContext = propertySym->m_func->GetScriptContext();
            Js::PropertyRecord const* fieldName = scriptContext->GetPropertyNameLocked(propertySym->m_propertyId);
            Output::Print(L"->%s", fieldName->GetBuffer());
            break;
        }
        case PropertyKindSlots:
        case PropertyKindSlotArray:
            propertySym->m_stackSym->Dump(flags, valueType);
            Output::Print(L"[%d]", propertySym->m_propertyId);
            break;
        case PropertyKindLocalSlots:
            propertySym->m_stackSym->Dump(flags, valueType);
            Output::Print(L"l[%d]", propertySym->m_propertyId);
            break;
        default:
            AssertMsg(0, "Unknown field kind");
            break;
        }

        if (!SimpleForm)
        {
            Output::Print(L")");
        }
    }
}
    JavascriptString * DynamicObjectPropertyEnumerator::MoveAndGetNextWithCache(PropertyId& propertyId, PropertyAttributes* attributes)
    {
        Assert(enumeratedCount <= cachedData->cachedCount);
        JavascriptString* propertyStringName;
        PropertyAttributes propertyAttributes = PropertyNone;
        if (enumeratedCount < cachedData->cachedCount)
        {
            PropertyString * propertyString = cachedData->strings[enumeratedCount];
            propertyStringName = propertyString;
            propertyId = propertyString->GetPropertyRecord()->GetPropertyId();

#if ENABLE_TTD
            //
            //TODO: We have code in MoveAndGetNextFromObject to record replay the order in which properties are enumerated. 
            //      Since caching may happen differently at record/replay time we need to force this to ensure the log/order is consistent.
            //      Later we may want to optimize by lifting the TTD code from the call and explicitly calling it here (but not the rest of the enumeration work).
            //
            Js::ScriptContext* actionCtx = this->object->GetScriptContext();
            if (actionCtx->ShouldPerformRecordAction() | actionCtx->ShouldPerformDebugAction())
            {
                PropertyId tempPropertyId;
                /* JavascriptString * tempPropertyString = */ this->MoveAndGetNextNoCache(tempPropertyId, attributes);

                Assert(tempPropertyId == propertyId);
                Assert(this->objectIndex == cachedData->indexes[enumeratedCount]);
            }
#elif DBG
            PropertyId tempPropertyId;
            /* JavascriptString * tempPropertyString = */ this->MoveAndGetNextNoCache(tempPropertyId, attributes);

            Assert(tempPropertyId == propertyId);
            Assert(this->objectIndex == cachedData->indexes[enumeratedCount]);
#endif

            this->objectIndex = cachedData->indexes[enumeratedCount];
            propertyAttributes = cachedData->attributes[enumeratedCount];

            enumeratedCount++;
        }
        else if (!cachedData->completed)
        {
            propertyStringName = this->MoveAndGetNextNoCache(propertyId, &propertyAttributes);

            if (propertyStringName && VirtualTableInfo<PropertyString>::HasVirtualTable(propertyStringName))
            {
                Assert(enumeratedCount < this->initialPropertyCount);
                cachedData->strings[enumeratedCount] = (PropertyString*)propertyStringName;
                cachedData->indexes[enumeratedCount] = this->objectIndex;
                cachedData->attributes[enumeratedCount] = propertyAttributes;
                cachedData->cachedCount = ++enumeratedCount;
            }
            else
            {
                cachedData->completed = true;
            }
        }
        else
        {
#if ENABLE_TTD
            //
            //TODO: We have code in MoveAndGetNextFromObject to record replay the order in which properties are enumerated. 
            //      Since caching may happen differently at record/replay time we need to force this to ensure the log/order is consistent.
            //      Later we may want to optimize by lifting the TTD code from the call and explicitly calling it here (but not the rest of the enumeration work).
            //
            Js::ScriptContext* actionCtx = this->object->GetScriptContext();
            if (actionCtx->ShouldPerformRecordAction() | actionCtx->ShouldPerformDebugAction())
            {
                PropertyId tempPropertyId;
                /*JavascriptString* tempPropertyStringName =*/ this->MoveAndGetNextNoCache(tempPropertyId, attributes);
            }
#elif DBG
            PropertyId tempPropertyId;
            Assert(this->MoveAndGetNextNoCache(tempPropertyId, attributes) == nullptr);
#endif

            propertyStringName = nullptr;
        }

        if (attributes != nullptr)
        {
            *attributes = propertyAttributes;
        }

        return propertyStringName;
    }