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 }
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; }
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 }
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 }
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); } } }
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; }
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 }
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; }
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 }
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; }