using System; using System.Collections; using System.Text; using System.Diagnostics; using System.Threading.Tasks; using Beefy; using Beefy.gfx; using Beefy.widgets; using Beefy.theme.dark; using Beefy.utils; using Beefy.geom; using IDE.Compiler; using IDE.Debugger; using System.IO; using IDE.util; namespace IDE.ui { public class DisassemblyEditContent : SourceEditWidgetContent { public DisassemblyPanel mDisassemblyPanel; public float mJmpIconY; public int mJmpState = -1; public override void MouseClicked(float x, float y, float origX, float origY, int32 btn) { base.MouseClicked(x, y, origX, origY, btn); if (btn == 1) { //var sourceViewPanel = (SourceViewPanel)mParent.mParent; Menu menu = new Menu(); if (!String.IsNullOrEmpty(mDisassemblyPanel.mSourceFileName)) { var menuItem = menu.AddItem("Go to Source"); menuItem.mOnMenuItemSelected.Add(new (evt) => mDisassemblyPanel.GoToSource()); } /*menu.AddItem("Item 2"); menu.AddItem(); menu.AddItem("Item 3");*/ MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu); if (menu.mItems.Count > 0) menuWidget.Init(this, x, y); else delete menuWidget; //menuWidget.mWidgetWindow.mWindowClosedHandler += MenuClosed; } /*if (btn == 0) { let fileName = scope String(); int addr = mDisassemblyPanel.GetCursorAddress(fileName); Debug.WriteLine("{0} : {1:X}", fileName, addr); }*/ } public override void Draw(Graphics g) { base.Draw(g); float lineSpacing = mFont.GetLineSpacing(); int32 cursorLine = CursorLineAndColumn.mLine; for (int32 pass = 0; pass < 2; pass++) { for (var jumpEntry in mDisassemblyPanel.mJumpTable) { var minLineResult = mDisassemblyPanel.mAddrLines.GetValue(jumpEntry.mAddrMin); var maxLineResult = mDisassemblyPanel.mAddrLines.GetValue(jumpEntry.mAddrMax); if ((minLineResult case .Ok) && (maxLineResult case .Ok)) { int32 minLine = minLineResult; int32 maxLine = maxLineResult; bool isHilighted = (cursorLine == minLine) || (cursorLine == maxLine); if (isHilighted != (pass == 1)) continue; uint32 color = jumpEntry.mIsReverse ? (isHilighted ? 0xFFffeafe : 0xFF8a7489) : (isHilighted ? 0xFFeaf5ff : 0xFF72828f); float minY = 0 + minLine * lineSpacing + GS!(6); float maxY = 0 + maxLine * lineSpacing + GS!(6); float width = GS!(21) + jumpEntry.mDepth * GS!(7); using (g.PushColor(color)) { //g.FillRect(50 - width, minY - 4, width, maxY - minY + 7); //g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.AsmArrow), 50 - width, minY - 4, width, maxY - minY + 7); if (!jumpEntry.mIsReverse) { g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.AsmArrow), GS!(50) - width, minY - GS!(4), width, maxY - minY + GS!(7)); } else { g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.AsmArrowRev), GS!(50) - width, minY - GS!(4), width, maxY - minY + GS!(7)); } } for (var overlapDepth in jumpEntry.mOverlapsAtMin) { g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.AsmArrowShadow), GS!(26) - overlapDepth * GS!(7), minY - GS!(10)); } for (var overlapDepth in jumpEntry.mOverlapsAtMax) { g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.AsmArrowShadow), GS!(26) - overlapDepth * GS!(7), maxY - GS!(10)); } } } } if (mJmpState != -1) { var img = (mJmpState == 0) ? DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.NoJmp) : DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.YesJmp); g.Draw(img, 34, mJmpIconY); } } } public class DisassemblyEdit : SourceEditWidget { public this() : base(null, new DisassemblyEditContent()) { } } public class DisassemblyPanel : TextPanel { public struct LineData { public int mAddr; public int mAddrEnd; public String mSourceFile; public int32 mSourceLineNum; } public class JumpEntry { public int mAddrMin; public int mAddrMax; public bool mIsReverse; public int32 mDepth; public List mOverlapsAtMin = new List() ~ delete _; public List mOverlapsAtMax = new List() ~ delete _; } public static String sPanelName = "Disassembly"; public DisassemblyEdit mEditWidget; PanelHeader mPanelHeader; //string[] mSourceLines; List mLineStarts ~ delete _; EditWidgetContent.CharData[] mCharData ~ delete _; String mDiasmSourceFileName ~ delete _; List mLineDatas = new List() ~ delete _; public List mJumpTable = new List() ~ DeleteContainerAndItems!(_); public Dictionary mAddrLines = new Dictionary() ~ delete _; public bool mIsInitialized; Dictionary mBreakpointAddrs = new .() ~ delete _; public bool mLocationDirty; private int mDeferredAddr; public DarkCheckBox mStayInDisassemblyCheckbox; public int32 mSourceStackIdx; int32 mSourceLine; int32 mSourceColumn; int32 mHotIdx; int32 mDefLineStart; int32 mDefLineEnd; bool mIsRawDiassembly; public String mSourceFileName ~ delete _; public String mAliasFilePath ~ delete _; public SourceHash mSourceHash; public this() { mEditWidget = new DisassemblyEdit(); var content = (DisassemblyEditContent)mEditWidget.Content; content.mDisassemblyPanel = this; content.mIsMultiline = true; content.mIsReadOnly = true; content.mWordWrap = false; content.mTextInsets.mLeft = 48; content.SetFont(IDEApp.sApp.mCodeFont, true, true); content.mTextColors = SourceEditWidgetContent.sTextColors; mEditWidget.InitScrollbars(true, true); AddWidget(mEditWidget); Disable(); } public ~this() { RemovePanelHeader(); if (mStayInDisassemblyCheckbox != null) delete mStayInDisassemblyCheckbox; } public override SourceEditWidget EditWidget { get { return mEditWidget; } } public override void Serialize(StructuredData data) { base.Serialize(data); data.Add("Type", "DisassemblyPanel"); } public override bool Deserialize(StructuredData data) { return base.Deserialize(data); } public void FocusEdit() { mEditWidget.SetFocus(); } public void ClearQueuedData() { mDeferredAddr = 0; } void SetFile(String fileName, out bool isOld) { mSourceHash = .(); isOld = false; String useFileName = scope String(fileName); if (mAliasFilePath != null) { if (Path.Equals(useFileName, mAliasFilePath)) useFileName.Set(mSourceFileName); } IDEUtils.FixFilePath(useFileName); String.NewOrSet!(mDiasmSourceFileName, useFileName); var app = IDEApp.sApp; delete mLineStarts; delete mCharData; mLineStarts = null; mCharData = null; String text = null; ProjectSource projectSource = app.FindProjectSourceItem(useFileName); if (projectSource != null) { IdSpan liveCharIdData; String liveText = scope:: String(); app.FindProjectSourceContent(projectSource, out liveCharIdData, true, liveText, null); defer(stack) liveCharIdData.Dispose(); var compileInstance = IDEApp.sApp.mWorkspace.GetProjectSourceCompileInstance(projectSource, mHotIdx); if (compileInstance == null) { text = liveText; } else { text = compileInstance.mSource; int32 char8IdStart; int32 char8IdEnd; IdSpan char8IdData = IDEApp.sApp.mWorkspace.GetProjectSourceSelectionCharIds(projectSource, mHotIdx, mDefLineStart, mDefLineEnd, out char8IdStart, out char8IdEnd); if (!char8IdData.IsEmpty) { if (!char8IdData.IsRangeEqual(liveCharIdData, (int32)char8IdStart, (int32)char8IdEnd)) { isOld = true; //var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content; //sourceEditWidgetContent.SetOldVersionColors(true); } } } //var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content; //sourceEditWidgetContent.SetOldVersionColors(mHotIdx != -1); //app.mWorkspace. } else { text = scope:: String(); if (gApp.LoadTextFile(useFileName, text) case .Err) text = null; } if (text != null) { mCharData = new EditWidgetContent.CharData[text.Length]; for (int32 i = 0; i < mCharData.Count; i++) { mCharData[i].mChar = (char8)text[i]; } } bool isBeefSource = IDEApp.IsBeefFile(useFileName); var bfSystem = app.mBfResolveSystem; if (bfSystem != null) { //var compiler = app.mBfResolveCompiler; BfProject bfProject = null; if (projectSource != null) bfProject = bfSystem.GetBfProject(projectSource.mProject); if (text != null) { var parser = bfSystem.CreateEmptyParser(bfProject); var resolvePassData = parser.CreateResolvePassData(); var passInstance = bfSystem.CreatePassInstance(); parser.SetIsClassifying(); parser.SetSource(text, useFileName); parser.Parse(passInstance, !isBeefSource); if (isBeefSource) parser.Reduce(passInstance); parser.ClassifySource(mCharData, !isBeefSource); delete resolvePassData; delete parser; delete passInstance; mLineStarts = new List(); mLineStarts.Add(0); for (int32 i = 0; i < text.Length; i++) if (mCharData[i].mChar == '\n') mLineStarts.Add(i + 1); mLineStarts.Add((int32)text.Length + 1); } } } /*string GetSourceLine(int lineNum) { if (lineNum >= mSourceLines.Length) return null; return mSourceLines[lineNum]; }*/ ////// public void GoToSource() { if (String.IsNullOrEmpty(mSourceFileName)) { IDEApp.sApp.Fail("Unable to locate source"); return; } #unwarn var sourceViewPanel = gApp.ShowSourceFileLocation(mSourceFileName, -1, IDEApp.sApp.mWorkspace.GetHighestCompileIdx(), mSourceLine, mSourceColumn, LocatorType.None); if (sourceViewPanel.mLoadFailed) sourceViewPanel.mWantHash = mSourceHash; /*if (sourceViewPanel.LoadedHash case .None) sourceViewPanel.LoadedHash = mSourceHash; // Set for when we do Auto Find*/ } public bool SelectLine(int addr, bool jumpToPos) { var editContent = mEditWidget.Content; if (editContent.mData.mLineStarts == null) return false; //FocusEdit(); for (int32 lineIdx = 0; lineIdx < mLineDatas.Count; lineIdx++) { var lineData = mLineDatas[lineIdx]; if ((lineData.mAddr != (int)0) && (addr >= lineData.mAddr) && (addr < lineData.mAddrEnd)) { if (lineIdx >= editContent.mData.mLineStarts.Count) { // Wtf? editContent.CursorTextPos = editContent.mData.mTextLength; } else editContent.CursorTextPos = editContent.mData.mLineStarts[lineIdx]; editContent.EnsureCursorVisible(true, true); if (jumpToPos) // Jump to whatever position we're scrolling to { mEditWidget.mVertPos.mPct = 1.0f; mEditWidget.UpdateContentPosition(); } return true; } } return false; } public int GetCursorAddress(String outSourceFileName) { int line; int lineChar; mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out line, out lineChar); line = Math.Min(line, (int32)mLineDatas.Count - 1); for (int checkLine = line; checkLine >= 0; checkLine--) { var lineData = mLineDatas[checkLine]; if (lineData.mSourceFile != null) { outSourceFileName.Append(lineData.mSourceFile); break; } } for (int checkLine = line; checkLine < mLineDatas.Count; checkLine++) { var lineData = mLineDatas[checkLine]; if (lineData.mAddr != (int)0) return lineData.mAddr; } for (int checkLine = line - 1; checkLine >= 0; checkLine--) { var lineData = mLineDatas[checkLine]; if (lineData.mAddr != (int)0) return lineData.mAddr; } return (int)0; } /*public void Show(int addr, String sourceFile = null, int32 sourceLine = -1, int32 sourceColumn = -1, int32 hotIdx = -1, int32 defLineStart = -1, int32 defLineEnd = -1) { String codeData = scope String(); var lines = String.StackSplit!(codeData, '\n'); }*/ void RemovePanelHeader() { if (mStayInDisassemblyCheckbox != null) mStayInDisassemblyCheckbox.RemoveSelf(); if (mPanelHeader != null) { mPanelHeader.RemoveSelf(); delete mPanelHeader; } } public void Show(int addr, String sourceFile = null, int sourceLine = -1, int sourceColumn = -1, int hotIdx = -1, int defLineStart = -1, int defLineEnd = -1) { if (sourceFile == null) DeleteAndNullify!(mSourceFileName); else String.NewOrSet!(mSourceFileName, sourceFile); mSourceLine = (int32)sourceLine; mSourceColumn = (int32)sourceColumn; mHotIdx = (int32)hotIdx; mDefLineStart = (int32)defLineStart; mDefLineEnd = (int32)defLineEnd; mSourceHash = .(); if (mParent == null) { mDeferredAddr = addr; return; } mDeferredAddr = 0; //bool wasChecked = false; RemovePanelHeader(); mPanelHeader = new PanelHeader(); mPanelHeader.Label = "Showing disassembly."; if (mStayInDisassemblyCheckbox == null) mStayInDisassemblyCheckbox = new DarkCheckBox(); mStayInDisassemblyCheckbox.Label = "Stay in dis&assembly mode."; //if (mStayInDisassemblyCheckbox != null) //checkbox.mChecked = mStayInDisassemblyCheckbox.mChecked; mPanelHeader.AddWidget(mStayInDisassemblyCheckbox); //mStayInDisassemblyCheckbox = checkbox; if (!String.IsNullOrEmpty(mSourceFileName)) { var button = mPanelHeader.AddButton("Show Sour&ce"); button.mOnMouseClick.Add(new (evt) => GoToSource()); } AddWidget(mPanelHeader); mIsInitialized = true; ResizeComponents(); if ((mIsRawDiassembly) && (String.IsNullOrEmpty(sourceFile))) { if (SelectLine(addr, false)) return; } var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content; sourceEditWidgetContent.SetOldVersionColors(false); Clear(); mIsRawDiassembly = false; mEditWidget.SetText(""); mLineDatas.Clear(); String codeData = scope String(); if (addr != 0) IDEApp.sApp.mDebugger.DisassembleAt(addr, codeData); int prevDisasmLineData = -1; int lineIdx = 0; bool allowEmptyLine = false; bool allowOldColoring = true; //var lines = String.StackSplit!(codeData, '\n'); String line = scope String(); //char8[] seps = scope char8[] { '\n' }; //for (var lineStrView in codeData.Split(seps)) int checkIdx = 0; bool hadSourceLines = false; bool isOptimized = false; String sourceLineText = scope .(1024); for (var lineStrView in codeData.Split('\n')) { checkIdx++; line.Reference(lineStrView); if (line.Length == 0) break; int32 prevLen = mEditWidget.Content.mData.mTextLength; int32 typeId = -1; LineData lineData = .(); bool addLineData = true; bool nextAllowEmptyLine = false; switch (line[0]) { case 'O': isOptimized = true; addLineData = false; case 'R': mIsRawDiassembly = true; addLineData = false; case 'S': { typeId = (int32)SourceElementType.Disassembly_FileName; String fileName = scope String(line, 2); bool isOld; SetFile(fileName, out isOld); String text = scope String("--- ", fileName); if (isOld) { text.Append(" (old version)"); if (allowOldColoring) { sourceEditWidgetContent.SetOldVersionColors(true); } } else { allowOldColoring = false; } text.Append("\n"); mEditWidget.Content.InsertText(prevLen, text); lineIdx++; } break; case 'H': { mSourceHash = SourceHash.Create(.(line, 2)); addLineData = false; } case 'T': { typeId = (int32)SourceElementType.Disassembly_FileName; String lineText = scope String(line, 2); lineText.Append("\n"); mEditWidget.Content.InsertText(prevLen, lineText); lineIdx++; } break; case 'D': { typeId = (int32)SourceElementType.Disassembly_Text; String disasmLine = scope String(line, 2); disasmLine.Append("\n"); mEditWidget.Content.InsertText(prevLen, disasmLine); int32 parenPos = (int32)disasmLine.IndexOf(':'); //int ptrSize = 8; var addrString = scope String(disasmLine, 0, parenPos); addrString.Replace("'", ""); lineData.mAddr = (int)int64.Parse(addrString, System.Globalization.NumberStyles.HexNumber); lineData.mAddrEnd = lineData.mAddr + 1; if (prevDisasmLineData != -1) { var prevLineData = mLineDatas[prevDisasmLineData]; prevLineData.mAddrEnd = lineData.mAddr; mLineDatas[prevDisasmLineData] = prevLineData; } prevDisasmLineData = mLineDatas.Count; mAddrLines[lineData.mAddr] = (int32)lineIdx; lineIdx++; } break; case 'L': { typeId = -1; var itr = line.Split(' '); itr.MoveNext(); int32 sourceLineStart = int32.Parse(itr.GetNext()); int32 sourceLineCount = int32.Parse(itr.GetNext()); if ((mLineStarts == null) || (sourceLineStart < 0) || (sourceLineStart + sourceLineCount >= mLineStarts.Count - 1)) { //mEditWidget.Content.InsertText(prevLen, "- Error -\n"); continue; } void ExtractLine(int sourceLineNum, String destString, out int32 startIdx, out int32 endIdx) { destString.Clear(); startIdx = mLineStarts[sourceLineNum]; endIdx = mLineStarts[sourceLineNum + 1] - 1; char8[] chars = scope char8[endIdx - startIdx]; for (int32 idx = startIdx; idx < endIdx; idx++) chars[idx - startIdx] = (char8)mCharData[idx].mChar; destString.Append(chars, 0, chars.Count); } if ((!hadSourceLines) && (!isOptimized)) { // Try to trim off the bottom of the preceding method for (int32 checkLine = sourceLineStart + sourceLineCount - 2; checkLine >= sourceLineStart; checkLine--) { ExtractLine(checkLine, sourceLineText, var startIdx, var endIdx); if ((sourceLineText.Contains("}")) || (sourceLineText.Contains(";"))) { int32 offset = checkLine - sourceLineStart + 1; sourceLineStart += offset; sourceLineCount -= offset; break; } } } for (int32 sourceLineNum = sourceLineStart; sourceLineNum < sourceLineStart + sourceLineCount; sourceLineNum++) { //int32 startIdx; //int32 endIdx; ExtractLine(sourceLineNum, sourceLineText, var startIdx, var endIdx); if ((!allowEmptyLine) && (sourceLineText.IsWhiteSpace)) { continue; } allowEmptyLine = true; sourceLineText.Append("\n"); prevLen = mEditWidget.Content.mData.mTextLength; mEditWidget.Content.InsertText(prevLen, sourceLineText); for (int32 i = 0; i < endIdx - startIdx; i++) mEditWidget.Content.mData.mText[i + prevLen].mDisplayTypeId = mCharData[i + startIdx].mDisplayTypeId; lineData = .(); lineData.mSourceFile = mDiasmSourceFileName; lineData.mSourceLineNum = sourceLineNum; lineIdx++; mLineDatas.Add(lineData); } // Just about the only case we allow an empty line is when it occurs after a non-empty source line nextAllowEmptyLine = true; addLineData = false; hadSourceLines = true; } break; case 'J': { addLineData = false; JumpEntry jumpEntry = new JumpEntry(); int64 addrFrom = (int64)mLineDatas[mLineDatas.Count - 1].mAddr; int64 addrTo = int64.Parse(scope String(line, 2), System.Globalization.NumberStyles.HexNumber); jumpEntry.mAddrMin = (int)Math.Min(addrFrom, addrTo); jumpEntry.mAddrMax = (int)Math.Max(addrFrom, addrTo); jumpEntry.mIsReverse = jumpEntry.mAddrMin == (int)addrTo; jumpEntry.mDepth = -1; if (jumpEntry.mAddrMin != jumpEntry.mAddrMax) mJumpTable.Add(jumpEntry); else delete jumpEntry; } break; } allowEmptyLine = nextAllowEmptyLine; if (addLineData) { mLineDatas.Add(lineData); if (typeId != -1) { for (int32 i = prevLen; i <= mEditWidget.Content.mData.mTextLength; i++) mEditWidget.Content.mData.mText[i].mDisplayTypeId = (uint8)typeId; } } } mJumpTable.Sort(scope (a, b) => { int32 diff = (int32)((a.mAddrMax - a.mAddrMin) - (b.mAddrMax - b.mAddrMin)); if (diff != 0) return diff; return (int32)(a.mAddrMin - b.mAddrMin); }); List arrowDepths = scope List(); for (int32 jumpIdx = 0; jumpIdx < mJumpTable.Count; jumpIdx++) { var jumpEntry = mJumpTable[jumpIdx]; arrowDepths.Clear(); for (int32 checkJumpIdx = 0; checkJumpIdx < mJumpTable.Count; checkJumpIdx++) { var checkJumpEntry = mJumpTable[checkJumpIdx]; if ((checkJumpEntry.mDepth != -1) && (((checkJumpEntry.mAddrMin > jumpEntry.mAddrMin) && (checkJumpEntry.mAddrMin < jumpEntry.mAddrMax)) || ((checkJumpEntry.mAddrMax > jumpEntry.mAddrMin) && (checkJumpEntry.mAddrMax < jumpEntry.mAddrMax)))) arrowDepths.Add(checkJumpEntry.mDepth); } for (int32 checkDepth = 0; true; checkDepth++) { if (!arrowDepths.Contains(checkDepth)) { jumpEntry.mDepth = checkDepth; break; } } for (int32 checkJumpIdx = 0; checkJumpIdx < mJumpTable.Count; checkJumpIdx++) { var checkJumpEntry = mJumpTable[checkJumpIdx]; if ((checkJumpEntry.mDepth == -1) || (checkJumpEntry.mDepth >= jumpEntry.mDepth)) continue; if ((jumpEntry.mAddrMin > checkJumpEntry.mAddrMin) && (jumpEntry.mAddrMin < checkJumpEntry.mAddrMax)) jumpEntry.mOverlapsAtMin.Add(checkJumpEntry.mDepth); if ((jumpEntry.mAddrMax > checkJumpEntry.mAddrMin) && (jumpEntry.mAddrMax < checkJumpEntry.mAddrMax)) jumpEntry.mOverlapsAtMax.Add(checkJumpEntry.mDepth); } } mEditWidget.Content.ClampCursor(); mEditWidget.Content.ContentChanged(); mEditWidget.Content.RecalcSize(); //Debug.Assert(mLineDatas.Count == mEditWidget.Content.mLineStarts.Length); SelectLine(addr, true); // Go there immediately mEditWidget.mVertPos.mPct = 1.0f; mEditWidget.UpdateContentPosition(); if (mEditWidget.Content.mData.mTextLength == 0) { mEditWidget.SetText("Disassembly unavailable"); } //mEditWidget.Text = codeData; } public override void Draw(Beefy.gfx.Graphics g) { base.Draw(g); DarkEditWidgetContent darkEditWidgetContent = (DarkEditWidgetContent)mEditWidget.Content; g.SetFont(IDEApp.sApp.mTinyCodeFont); int curCallStackAddr = 0; int32 jmpState = -1; if (IDEApp.sApp.mExecutionPaused) { String fileName = null; IDEApp.sApp.mDebugger.GetStackFrameInfo(IDEApp.sApp.mDebugger.mActiveCallStackIdx, out curCallStackAddr, fileName, null); jmpState = IDEApp.sApp.mDebugger.GetJmpState(IDEApp.sApp.mDebugger.mActiveCallStackIdx); } mBreakpointAddrs.Clear(); for (var breakpoint in IDEApp.sApp.mDebugger.mBreakpointList) { void* curBreakpointItr = null; while (true) { int addr = breakpoint.GetAddress(ref curBreakpointItr); if (addr != (int)0) mBreakpointAddrs.TryAdd(addr, breakpoint); if (curBreakpointItr == null) break; } } String callstackFileName = scope String(Path.[Friend]MaxPath); int callstackLineNum = -1; Image linePointerImage = DarkTheme.sDarkTheme.GetImage(.LinePointer); if (IDEApp.sApp.mExecutionPaused) { int addr; int hotIdx; int defLineStart; int defLineEnd; //int lineNum; int column; int language; int stackSize; DebugManager.FrameFlags frameFlags; IDEApp.sApp.mDebugger.GetStackFrameInfo(IDEApp.sApp.mDebugger.mActiveCallStackIdx, null, out addr, callstackFileName, out hotIdx, out defLineStart, out defLineEnd, out callstackLineNum, out column, out language, out stackSize, out frameFlags); int hashPos = callstackFileName.IndexOf('#'); if (hashPos != -1) callstackFileName.RemoveToEnd(hashPos); if (frameFlags.HasFlag(.Optimized)) linePointerImage = DarkTheme.sDarkTheme.GetImage(.LinePointer_Opt); //IDEUtils.FixFilePath(fileName); if (IDEApp.sApp.mDebugger.mActiveCallStackIdx == 0) callstackLineNum = -1; } //jmpState = 0; var disasmEditContent = (DisassemblyEditContent)mEditWidget.Content; disasmEditContent.mJmpState = -1; using (g.PushClip(0, 0, mWidth, mHeight)) { using (g.PushTranslate(0, mEditWidget.mY + mEditWidget.Content.Y)) { float lineSpacing = darkEditWidgetContent.mFont.GetLineSpacing(); int lineStart = Math.Max(0, (int32)((-mEditWidget.Content.Y) / lineSpacing) - 1); int lineEnd = Math.Min(mLineDatas.Count, lineStart + (int32)(mHeight / lineSpacing) + 3); using (g.PushColor(0x80FFFFFF)) { for (int lineIdx = lineStart; lineIdx < lineEnd; lineIdx++) { var lineData = mLineDatas[lineIdx]; if (lineData.mSourceFile != null) g.DrawString(StackStringFormat!("{0}", lineData.mSourceLineNum + 1), GS!(8), GS!(2) + lineIdx * lineSpacing, FontAlign.Right, GS!(18)); } } for (int lineIdx = lineStart; lineIdx < lineEnd; lineIdx++) { var lineData = mLineDatas[lineIdx]; if ((lineData.mAddr != (int)0) && (mBreakpointAddrs.GetValue(lineData.mAddr) case .Ok(let breakpoint))) { //g.Draw(DarkTheme.sDarkTheme.GetImage(.RedDot), mEditWidget.mX - 20, //0 + lineIdx * lineSpacing); using (g.PushTranslate(mEditWidget.mX - GS!(20), 0 + lineIdx * lineSpacing)) breakpoint.Draw(g, false); } if ((lineData.mAddr != (int)0) && (curCallStackAddr >= lineData.mAddr) && (curCallStackAddr < lineData.mAddrEnd)) { Image img = (IDEApp.sApp.mDebugger.mActiveCallStackIdx == 0) ? linePointerImage : DarkTheme.sDarkTheme.GetImage(.ReturnPointer); g.Draw(img, mEditWidget.mX - GS!(20), 0 + lineIdx * lineSpacing); /*if (jmpState != -1) { img = (jmpState == 0) ? DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.NoJmp) : DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.YesJmp); g.Draw(img, mEditWidget.mX + 20, 0 + lineIdx * lineSpacing); }*/ disasmEditContent.mJmpState = jmpState; disasmEditContent.mJmpIconY = -4 + lineIdx * lineSpacing; } if ((lineData.mSourceFile != null) && (lineData.mSourceLineNum == callstackLineNum) && (Path.Equals(lineData.mSourceFile, callstackFileName))) { //RemapCompiledToActiveLine(hotIdx, ref lineNum, ref column); Image img = (IDEApp.sApp.mDebugger.mActiveCallStackIdx == 0) ? linePointerImage : DarkTheme.sDarkTheme.GetImage(.LinePointer_Prev); g.Draw(img, mEditWidget.mX - GS!(20), 0 + lineIdx * lineSpacing); } } } } } /*public IEnumerable FindTrackedElementsAtCursor(List trackedElementList) where T : TrackedTextElement { int lineIdx; int lineCharIdx; mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx); int idx = 0; while (idx < trackedElementList.Count) { var trackedElement = trackedElementList[idx]; if (trackedElement.mLineNum == lineIdx) { int prevSize = trackedElementList.Count; yield return trackedElement; if (trackedElementList.Count >= prevSize) idx++; } else idx++; } }*/ // This is useful for a one-shot breakpoint within the current execution context- IE: "Run to cursor" public Breakpoint ToggleAddrBreakpointAtCursor(Breakpoint.SetKind setKind = .Toggle, Breakpoint.SetFlags flags = .None, int threadId = -1) { DebugManager debugManager = IDEApp.sApp.mDebugger; int lineIdx; int lineCharIdx; mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx); int checkIdx = lineIdx; LineData lineData = mLineDatas[checkIdx]; if (lineData.mAddr == (int)0) return null; bool hadBreakpoint = false; if (setKind != .Force) { for (int32 breakIdx = 0; breakIdx < IDEApp.sApp.mDebugger.mBreakpointList.Count; breakIdx++) { var breakpoint = IDEApp.sApp.mDebugger.mBreakpointList[breakIdx]; if (breakpoint.ContainsAddress(lineData.mAddr)) { hadBreakpoint = true; BfLog.LogDbg("DisassemblyPanel.ToggleAddrBreakpointAtCursor\n"); debugManager.DeleteBreakpoint(breakpoint); breakIdx--; } } } if (!hadBreakpoint) { var editWidgetContent = mEditWidget.Content; int textPos = mEditWidget.Content.CursorTextPos - lineCharIdx; lineCharIdx = 0; // Find first non-space char8 while ((textPos < editWidgetContent.mData.mTextLength) && (((char8)editWidgetContent.mData.mText[textPos].mChar).IsWhiteSpace)) { textPos++; lineCharIdx++; } let breakpoint = debugManager.CreateBreakpoint(lineData.mAddr); breakpoint.SetThreadId(threadId); return breakpoint; } return null; } public Breakpoint ToggleBreakpointAtCursor(Breakpoint.SetKind setKind = .Toggle, Breakpoint.SetFlags flags = .None, int threadId = -1) { DebugManager debugManager = IDEApp.sApp.mDebugger; int lineIdx; int lineCharIdx; mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx); int checkIdx = lineIdx; int instrOffsetCount = -1; LineData lineData; if (checkIdx >= mLineDatas.Count) return null; while (true) { if (checkIdx < 0) { return ToggleAddrBreakpointAtCursor(setKind, flags, threadId); } lineData = mLineDatas[checkIdx]; if (lineData.mSourceFile != null) { bool foundOrigin = false; // In the case of a split line (ie: a method call that contains an inlined method call as a param), // we search for the first instance of this line for (int originIdx = 0; originIdx <= lineIdx; originIdx++) { var originLineData = mLineDatas[originIdx]; if ((originLineData.mSourceFile != null) && (originLineData.mSourceFile == lineData.mSourceFile) && (originLineData.mSourceLineNum == lineData.mSourceLineNum)) { foundOrigin = true; } else if ((foundOrigin) && (originLineData.mAddr != 0)) { instrOffsetCount++; } } break; } checkIdx--; } //We DO need to allow "+0", otherwise we can't bind to the PRELUDE of a function // Is there any reason to allow explicitly binding to "+0"? /*if (instrOffsetCount == 0) instrOffsetCount = -1;*/ bool hadBreakpoint = false; if (setKind != .Force) { for (int32 breakIdx = 0; breakIdx < IDEApp.sApp.mDebugger.mBreakpointList.Count; breakIdx++) { var breakpoint = IDEApp.sApp.mDebugger.mBreakpointList[breakIdx]; if ((breakpoint.mFileName != null) && (Path.Equals(breakpoint.mFileName, lineData.mSourceFile)) && (breakpoint.mLineNum == lineData.mSourceLineNum) && (Math.Max(0, breakpoint.mInstrOffset) == Math.Max(0, instrOffsetCount))) { hadBreakpoint = true; BfLog.LogDbg("DisassemblyPanel.ToggleBreakpointAtCursor deleting breakpoint\n"); debugManager.DeleteBreakpoint(breakpoint); breakIdx--; } } } if (!hadBreakpoint) { var editWidgetContent = mEditWidget.Content; int textPos = mEditWidget.Content.CursorTextPos - lineCharIdx; lineCharIdx = 0; // Find first non-space char8 while ((textPos < editWidgetContent.mData.mTextLength) && (((char8)editWidgetContent.mData.mText[textPos].mChar).IsWhiteSpace)) { textPos++; lineCharIdx++; } let breakpoint = debugManager.CreateBreakpoint_Create(lineData.mSourceFile, lineData.mSourceLineNum, 0, instrOffsetCount); breakpoint.SetThreadId(threadId); debugManager.CreateBreakpoint_Finish(breakpoint); return breakpoint; } return null; } public override void Clear() { mEditWidget.SetText(""); mLineDatas.Clear(); ClearAndDeleteItems(mJumpTable); mAddrLines.Clear(); mBreakpointAddrs.Clear(); } public void Disable() { Clear(); mEditWidget.SetText("Disassembly is only available while debugging"); } public bool Show(String file, int lineNum, int column) { String addrs = scope String(); IDEApp.sApp.mDebugger.FindCodeAddresses(file, lineNum, column, true, addrs); if (addrs.Length == 0) { IDEApp.sApp.Fail("Unable to locate code address"); return false; } var addrEntries = String.StackSplit!(addrs, '\n'); for (var addrEntry in addrEntries) { if (addrEntry.Length == 0) break; var addrData = String.StackSplit!(addrEntry, '\t'); int addr = (int)int64.Parse(addrData[0], System.Globalization.NumberStyles.HexNumber); Show(addr, file, lineNum, column); break; } return true; } public override void AddedToParent() { var app = IDEApp.sApp; if (!app.mDebugger.IsPaused()) mDeferredAddr = (int)0; if (mDeferredAddr != (int)0) Show(mDeferredAddr, mSourceFileName, mSourceLine, mSourceColumn, mHotIdx, mDefLineStart, mDefLineEnd); app.AddToRecentDisplayedFilesList(sPanelName); //IDEApp.sApp.SetInDisassemblyView(true); } public override void RemovedFromParent(Widget previousParent, WidgetWindow window) { base.RemovedFromParent(previousParent, window); IDEApp.sApp.SetInDisassemblyView(false); } protected override void ResizeComponents() { base.ResizeComponents(); if (mPanelHeader != null) mPanelHeader.Resize(GS!(30), 0, Math.Max(mWidth - GS!(30), 0), GS!(32)); var font = DarkTheme.sDarkTheme.mSmallFont; if (mStayInDisassemblyCheckbox != null) mStayInDisassemblyCheckbox.Resize(font.GetWidth(mPanelHeader.mLabel) + GS!(30), GS!(6), mStayInDisassemblyCheckbox.CalcWidth(), GS!(20)); mEditWidget.Resize(GS!(30), GS!(32), Math.Max(mWidth - GS!(30), 0), Math.Max(mHeight - GS!(32), 0)); } public override void Resize(float x, float y, float width, float height) { base.Resize(x, y, width, height); ResizeComponents(); } public void UpdateMouseover() { if (!CheckAllowHoverWatch()) return; if (IDEApp.sApp.HasPopupMenus()) return; if ((mHoverWatch != null) && (mHoverWatch.mCloseDelay > 0)) return; var editWidgetContent = mEditWidget.Content; Point mousePos; bool mouseoverFired = DarkTooltipManager.CheckMouseover(editWidgetContent, 10, out mousePos); //CompilerBase compiler = ResolveCompiler; if ((mouseoverFired) || (mHoverWatch != null)) { var content = mEditWidget.Content; //int cursorPos = content.CursorTextPos; int line; int lineChar; float overflowX; content.GetLineCharAtCoord(mousePos.x, mousePos.y, out line, out lineChar, out overflowX); int cursorPos = content.GetTextIdx(line, lineChar); int leftIdx = -1; int rightIdx = -1; String debugExpr = null; if ((cursorPos < content.mData.mTextLength) && (!((char8)content.mData.mText[cursorPos].mChar).IsWhiteSpace)) { var typeId = content.mData.mText[cursorPos].mDisplayTypeId; if (typeId == (uint8)SourceElementType.Disassembly_Text) { int lineStart; int lineEnd; content.GetLinePosition(line, out lineStart, out lineEnd); String lineStr = scope String(); content.ExtractString(lineStart, lineEnd - lineStart, lineStr); int strIdx = 0; int colonPos = -1; int instrStartIdx = -1; int instrEndIdx = -1; int firstParamIdx = -1; int commaIdx = -1; for (; strIdx < lineStr.Length; strIdx++) { char8 c = lineStr[strIdx]; if (colonPos == -1) { if (c == ':') colonPos = strIdx; } else if (instrStartIdx == -1) { if (c.IsLower) instrStartIdx = strIdx; } else if (instrEndIdx == -1) { if (c.IsWhiteSpace) instrEndIdx = strIdx; } else if (firstParamIdx == -1) { if (!c.IsWhiteSpace) firstParamIdx = strIdx; } else if (commaIdx == -1) { if (c == ',') commaIdx = strIdx; } } int cursorStrIdx = cursorPos - lineStart; if (cursorStrIdx > commaIdx) { if (commaIdx != -1) leftIdx = lineStart + commaIdx + 1; else leftIdx = lineStart + firstParamIdx; rightIdx = lineEnd - 1; } else if (cursorStrIdx >= firstParamIdx) { leftIdx = lineStart + firstParamIdx; if (commaIdx != -1) rightIdx = lineStart + commaIdx - 1; else rightIdx = lineEnd - 1; } if (leftIdx != -1) { debugExpr = scope:: String(); content.ExtractString(leftIdx, rightIdx - leftIdx + 1, debugExpr); int commaPos = debugExpr.LastIndexOf(','); if (commaPos != -1) debugExpr.RemoveToEnd(commaPos); int32 semiPos = (int32)debugExpr.IndexOf(';'); if (semiPos != -1) debugExpr.RemoveToEnd(semiPos); debugExpr.Replace('[', '('); debugExpr.Replace(']', ')'); debugExpr.Replace("xmmword ptr", "(int64[2]*)"); debugExpr.Replace("qword ptr", "(int64*)"); debugExpr.Replace("dword ptr", "(int32*)"); debugExpr.Replace("word ptr", "(int16*)"); debugExpr.Replace("byte ptr", "(int8*)"); if (line < mLineDatas.Count - 1) { if (mLineDatas[line].mAddrEnd != (int)0) { String nextAddr = StackStringFormat!("0x{0:x}L", (int64)mLineDatas[line].mAddrEnd); nextAddr.Append(" +"); debugExpr.Replace("rip +", nextAddr); } } if (commaIdx != -1) { String firstParam = lineStr; semiPos = (int32)lineStr.IndexOf(';'); if (semiPos != -1) debugExpr = scope:: String(lineStr, 0, semiPos); if (firstParam.Contains("xmm")) { debugExpr.Replace("(int64*)", "(double*)"); debugExpr.Replace("(int32*)", "(float*)"); } } } } else if ((typeId != (uint8)SourceElementType.Comment) && (typeId != (uint8)SourceElementType.Literal)) { leftIdx = cursorPos; rightIdx = cursorPos; while (leftIdx > 0) { int checkIdx = leftIdx - 1; char8 c = (char8)content.mData.mText[checkIdx].mChar; if ((!c.IsLetterOrDigit) && (c != '.')) break; leftIdx = checkIdx; } while (rightIdx < content.mData.mTextLength) { int checkIdx = rightIdx + 1; char8 c = (char8)content.mData.mText[checkIdx].mChar; if ((!c.IsLetterOrDigit) && (c != '.')) break; rightIdx = checkIdx; } } } if (leftIdx != -1) { content.GetLineCharAtIdx(leftIdx, out line, out lineChar); float x; float y; editWidgetContent.GetTextCoordAtLineChar(line, lineChar, out x, out y); x = mousePos.x; if (debugExpr == null) { debugExpr = scope:: String(); content.ExtractString(leftIdx, rightIdx - leftIdx + 1, debugExpr); } if ((mHoverWatch == null) || (mHoverWatch.mEvalString != debugExpr)) { if (mHoverWatch != null) { mHoverWatch.Close(); } else { mHoverWatch = new HoverWatch(); if (mHoverWatch.Show(this, x, y, debugExpr, debugExpr)) { mHoverWatch.mOpenMousePos = DarkTooltipManager.sLastRelMousePos; mHoverWatch.mEvalString.Set(debugExpr); // Set to old debugStr for comparison } else { mHoverWatch.Close(); } } } } else if (mHoverWatch != null) { mHoverWatch.Close(); } } } public override void Update() { base.Update(); if (mEditWidget.mHasFocus) IDEApp.sApp.SetInDisassemblyView(true); UpdateMouseover(); } } }