mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-18 09:29:42 +01:00
disassembly view: initial commit
This commit is contained in:
committed by
Yue (Felix) Huang
parent
1cbd1c6b65
commit
45bdf6bb6f
@@ -16,7 +16,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import {
|
||||
IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, DISASSEMBLY_VIEW_ID,
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
|
||||
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
|
||||
@@ -50,6 +50,9 @@ import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors';
|
||||
import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
|
||||
import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor';
|
||||
import { EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { DisassemblyView, DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView';
|
||||
|
||||
const debugCategory = nls.localize('debugCategory', "Debug");
|
||||
registerColors();
|
||||
@@ -373,6 +376,12 @@ viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('brea
|
||||
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
|
||||
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
|
||||
|
||||
// Register disassmelby editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")),
|
||||
[new SyncDescriptor(DisassemblyViewInput)]
|
||||
);
|
||||
|
||||
// Register configuration
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAda
|
||||
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema } from 'vs/workbench/contrib/debug/common/debugSchemas';
|
||||
import { launchSchema, debuggersExtPoint, breakpointsExtPoint, presentationSchema, disassemblyExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
@@ -41,6 +41,7 @@ export class AdapterManager implements IAdapterManager {
|
||||
private readonly _onDidRegisterDebugger = new Emitter<void>();
|
||||
private readonly _onDidDebuggersExtPointRead = new Emitter<void>();
|
||||
private breakpointModeIdsSet = new Set<string>();
|
||||
private disassemblyModeIdsSet = new Set<string>();
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@@ -155,6 +156,15 @@ export class AdapterManager implements IAdapterManager {
|
||||
added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language));
|
||||
});
|
||||
});
|
||||
|
||||
disassemblyExtPoint.setHandler((extensions, delta) => {
|
||||
delta.removed.forEach(removed => {
|
||||
removed.value.forEach(disassembly => this.disassemblyModeIdsSet.delete(disassembly.language));
|
||||
});
|
||||
delta.added.forEach(added => {
|
||||
added.value.forEach(disassembly => this.disassemblyModeIdsSet.add(disassembly.language));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { raceTimeout } from 'vs/base/common/async';
|
||||
import { registerAction2, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/browser/disassemblyView';
|
||||
|
||||
class ToggleBreakpointAction extends EditorAction2 {
|
||||
constructor() {
|
||||
@@ -133,6 +134,33 @@ class LogPointAction extends EditorAction2 {
|
||||
}
|
||||
}
|
||||
|
||||
class GoToDisassemblyAction extends EditorAction {
|
||||
|
||||
public static readonly ID = 'editor.debug.action.goToDisassembly';
|
||||
public static readonly LABEL = nls.localize('goToDisassembly', "Go to Disassembly");
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: GoToDisassemblyAction.ID,
|
||||
label: GoToDisassemblyAction.LABEL,
|
||||
alias: 'Debug: Go to Disassembly',
|
||||
precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus),
|
||||
contextMenuOpts: {
|
||||
group: 'debug',
|
||||
order: 5
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): Promise<void> {
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel()) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
editorService.openEditor(DisassemblyViewInput.instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RunToCursorAction extends EditorAction {
|
||||
|
||||
public static readonly ID = 'editor.debug.action.runToCursor';
|
||||
@@ -504,6 +532,7 @@ class CloseExceptionWidgetAction extends EditorAction {
|
||||
registerAction2(ToggleBreakpointAction);
|
||||
registerAction2(ConditionalBreakpointAction);
|
||||
registerAction2(LogPointAction);
|
||||
registerEditorAction(GoToDisassemblyAction);
|
||||
registerEditorAction(RunToCursorAction);
|
||||
registerEditorAction(StepIntoTargetsAction);
|
||||
registerEditorAction(SelectionToReplAction);
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Dimension, append, $ } from 'vs/base/browser/dom';
|
||||
import { ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { WorkbenchTable } from 'vs/platform/list/browser/listService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { DISASSEMBLY_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug';
|
||||
|
||||
interface IDisassembledInstructionEntry {
|
||||
allowBreakpoint: boolean;
|
||||
isBreakpointSet: boolean;
|
||||
instruction: DebugProtocol.DisassembledInstruction;
|
||||
}
|
||||
|
||||
export class DisassemblyView extends EditorPane {
|
||||
|
||||
private _editorOptions: IEditorOptions;
|
||||
private _disassembledInstructions: WorkbenchTable<IDisassembledInstructionEntry> | null;
|
||||
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super(DISASSEMBLY_VIEW_ID, telemetryService, themeService, storageService);
|
||||
|
||||
this._editorOptions = configurationService.getValue<IEditorOptions>('editor');
|
||||
configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('editor')) {
|
||||
this._editorOptions = configurationService.getValue<IEditorOptions>('editor');
|
||||
// TODO: refresh view
|
||||
}
|
||||
});
|
||||
|
||||
this._disassembledInstructions = null;
|
||||
}
|
||||
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
const lineHeight = this._editorOptions.lineHeight!;
|
||||
const delegate = new class implements ITableVirtualDelegate<IDisassembledInstructionEntry>{
|
||||
headerRowHeight: number = 0; // No header
|
||||
getHeight(row: IDisassembledInstructionEntry): number {
|
||||
return lineHeight;
|
||||
}
|
||||
};
|
||||
|
||||
this._disassembledInstructions = this._register(this._instantiationService.createInstance(WorkbenchTable,
|
||||
'DisassemblyView', parent, delegate,
|
||||
[
|
||||
{
|
||||
label: '',
|
||||
tooltip: '',
|
||||
weight: 0,
|
||||
minimumWidth: 40,
|
||||
maximumWidth: 40,
|
||||
templateId: BreakpointRenderer.TEMPLATE_ID,
|
||||
project(row: IDisassembledInstructionEntry): IDisassembledInstructionEntry { return row; }
|
||||
},
|
||||
{
|
||||
label: 'instructions',
|
||||
tooltip: '',
|
||||
weight: 0.3,
|
||||
templateId: InstructionRenderer.TEMPLATE_ID,
|
||||
project(row: IDisassembledInstructionEntry): IDisassembledInstructionEntry { return row; }
|
||||
},
|
||||
],
|
||||
[
|
||||
this._instantiationService.createInstance(BreakpointRenderer),
|
||||
this._instantiationService.createInstance(InstructionRenderer),
|
||||
],
|
||||
{
|
||||
identityProvider: { getId: (e: IDisassembledInstructionEntry) => e.instruction.address },
|
||||
horizontalScrolling: false,
|
||||
overrideStyles: {
|
||||
listBackground: editorBackground
|
||||
},
|
||||
multipleSelectionSupport: false,
|
||||
setRowLineHeight: false,
|
||||
openOnSingleClick: false,
|
||||
}
|
||||
)) as WorkbenchTable<IDisassembledInstructionEntry>;
|
||||
|
||||
this.loadDisassembledInstructions('0x00005000');
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
if (this._disassembledInstructions) {
|
||||
this._disassembledInstructions.layout(dimension.height);
|
||||
}
|
||||
}
|
||||
|
||||
private loadDisassembledInstructions(address: string): void {
|
||||
const newEntries: IDisassembledInstructionEntry[] = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
newEntries.push({ allowBreakpoint: true, isBreakpointSet: false, instruction: { address, instruction: 'instruction instruction, instruction' } });
|
||||
}
|
||||
if (this._disassembledInstructions) {
|
||||
// TODO: append/insert
|
||||
this._disassembledInstructions.splice(0, this._disassembledInstructions.length, newEntries);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface IBreakpointColumnTemplateData {
|
||||
icon: HTMLImageElement
|
||||
}
|
||||
|
||||
class BreakpointRenderer implements ITableRenderer<IDisassembledInstructionEntry, IBreakpointColumnTemplateData> {
|
||||
|
||||
static readonly TEMPLATE_ID = 'breakpoint';
|
||||
|
||||
templateId: string = BreakpointRenderer.TEMPLATE_ID;
|
||||
|
||||
renderTemplate(container: HTMLElement): IBreakpointColumnTemplateData {
|
||||
const icon = append(container, $<HTMLImageElement>('img.icon'));
|
||||
return { icon };
|
||||
}
|
||||
|
||||
renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void {
|
||||
if (element.isBreakpointSet) {
|
||||
// TODO: breakpoint icon
|
||||
}
|
||||
}
|
||||
disposeTemplate(templateData: IBreakpointColumnTemplateData): void { }
|
||||
|
||||
}
|
||||
|
||||
interface IInstructionColumnTemplateData {
|
||||
// TODO: hover widget?
|
||||
instruction: HTMLElement;
|
||||
}
|
||||
|
||||
class InstructionRenderer implements ITableRenderer<IDisassembledInstructionEntry, IInstructionColumnTemplateData> {
|
||||
|
||||
static readonly TEMPLATE_ID = 'instruction';
|
||||
|
||||
templateId: string = InstructionRenderer.TEMPLATE_ID;
|
||||
|
||||
renderTemplate(container: HTMLElement): IInstructionColumnTemplateData {
|
||||
const instruction = append(container, $('instruction'));
|
||||
return { instruction };
|
||||
}
|
||||
|
||||
renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void {
|
||||
const instruction = element.instruction;
|
||||
templateData.instruction.innerText = `${instruction.address}\t${instruction.instructionBytes}\t${instruction.instruction}`;
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IInstructionColumnTemplateData): void { }
|
||||
|
||||
}
|
||||
|
||||
export class DisassemblyViewInput extends EditorInput {
|
||||
|
||||
static readonly ID = 'debug.disassemblyView.input';
|
||||
|
||||
override get typeId(): string {
|
||||
return DisassemblyViewInput.ID;
|
||||
}
|
||||
|
||||
static _instance: DisassemblyViewInput;
|
||||
static get instance() {
|
||||
if (!DisassemblyViewInput._instance || DisassemblyViewInput._instance.isDisposed()) {
|
||||
DisassemblyViewInput._instance = new DisassemblyViewInput();
|
||||
}
|
||||
|
||||
return DisassemblyViewInput._instance;
|
||||
}
|
||||
|
||||
readonly resource = undefined;
|
||||
|
||||
override getName(): string {
|
||||
return localize('extensionsInputName', "Running Extensions");
|
||||
}
|
||||
|
||||
override canSplit(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
override matches(other: unknown): boolean {
|
||||
return other instanceof DisassemblyViewInput;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ export const WATCH_VIEW_ID = 'workbench.debug.watchExpressionsView';
|
||||
export const CALLSTACK_VIEW_ID = 'workbench.debug.callStackView';
|
||||
export const LOADED_SCRIPTS_VIEW_ID = 'workbench.debug.loadedScriptsView';
|
||||
export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView';
|
||||
export const DISASSEMBLY_VIEW_ID = 'workbench.debug.disassemblyView';
|
||||
export const DEBUG_PANEL_ID = 'workbench.panel.repl';
|
||||
export const REPL_VIEW_ID = 'workbench.panel.repl.view';
|
||||
export const DEBUG_SERVICE_ID = 'debugService';
|
||||
@@ -80,6 +81,7 @@ export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey<boolean>
|
||||
export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey<boolean>('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") });
|
||||
export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey<boolean>('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") });
|
||||
export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey<boolean>('multiSessionDebug', false, { type: 'boolean', description: nls.localize('multiSessionDebug', "True when there is more than 1 active debug session.") });
|
||||
export const CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED = new RawContextKey<boolean>('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") });
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
|
||||
|
||||
@@ -127,6 +127,31 @@ export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registe
|
||||
}
|
||||
});
|
||||
|
||||
export interface IDisassemblyContribution {
|
||||
language: string;
|
||||
}
|
||||
|
||||
// disassembly extension point
|
||||
export const disassemblyExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IDisassemblyContribution[]>({
|
||||
extensionPoint: 'disassembly',
|
||||
jsonSchema: {
|
||||
description: nls.localize('vscode.extension.contributes.disassembly', 'Contributes disassembly.'),
|
||||
type: 'array',
|
||||
defaultSnippets: [{ body: [{ language: '' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
defaultSnippets: [{ body: { language: '' } }],
|
||||
properties: {
|
||||
language: {
|
||||
description: nls.localize('vscode.extension.contributes.disassembly.language', "Allow disassembly view for this language."),
|
||||
type: 'string'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// debug general schema
|
||||
|
||||
export const presentationSchema: IJSONSchema = {
|
||||
|
||||
Reference in New Issue
Block a user