Skip to content

Commit

Permalink
Follow pointers (#122)
Browse files Browse the repository at this point in the history
Add command to go to value of a variable.
  • Loading branch information
gbodeen authored May 6, 2024
1 parent 835ef75 commit 36db026
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 8 deletions.
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
"title": "Show in Memory Inspector",
"category": "Memory"
},
{
"command": "memory-inspector.go-to-value",
"title": "Go to value in Memory Inspector",
"category": "Memory"
},
{
"command": "memory-inspector.toggle-variables-column",
"title": "Toggle Variables Column",
Expand Down Expand Up @@ -144,6 +149,10 @@
"command": "memory-inspector.show-variable",
"when": "canViewMemory && memory-inspector.canRead"
},
{
"command": "memory-inspector.go-to-value",
"when": "canViewMemory && memory-inspector.canRead && memory-inspector.variable.isPointer"
},
{
"command": "memory-inspector.store-file",
"when": "canViewMemory && memory-inspector.canRead"
Expand Down Expand Up @@ -199,6 +208,11 @@
"command": "memory-inspector.show-advanced-display-options",
"group": "z_more",
"when": "webviewId === memory-inspector.memory"
},
{
"command": "memory-inspector.go-to-value",
"group": "display@7",
"when": "webviewId === memory-inspector.memory && memory-inspector.variable.isPointer"
}
]
},
Expand Down
3 changes: 2 additions & 1 deletion src/common/debug-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export interface DebugRequestTypes {

export interface DebugEvents {
'memory': DebugProtocol.MemoryEvent,
'stopped': DebugProtocol.StoppedEvent
'stopped': DebugProtocol.StoppedEvent,
'output': DebugProtocol.OutputEvent,
}

export type DebugRequest<C, A> = Omit<DebugProtocol.Request, 'command' | 'arguments'> & { command: C, arguments: A };
Expand Down
1 change: 1 addition & 0 deletions src/common/memory-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export interface VariableMetadata {
type?: string;
/** If applicable, a string representation of the variable's value */
value?: string;
isPointer?: boolean;
}

/** Suitable for transmission as JSON */
Expand Down
10 changes: 10 additions & 0 deletions src/common/webview-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,13 @@ export function isWebviewContext(args: WebviewContext | unknown): args is Webvie
&& typeof assumed.showRadixPrefix === 'boolean' && typeof assumed.activeReadArguments?.count === 'number' && typeof assumed.activeReadArguments?.offset === 'number'
&& typeof assumed.activeReadArguments?.memoryReference === 'string';
}

export function isWebviewVariableContext(args: WebviewVariableContext | unknown): args is Required<WebviewVariableContext> {
const assumed = args ? args as WebviewVariableContext : undefined;
return !!assumed && isWebviewContext(args)
&& !!assumed.variable
&& typeof assumed.variable.name === 'string' && !!assumed.variable.name
&& (typeof assumed.variable.type === 'string' || assumed.variable.type === undefined)
&& (typeof assumed.variable.value === 'string' || assumed.variable.value === undefined)
&& (typeof assumed.variable.isPointer === 'boolean' || assumed.variable.isPointer === undefined);
}
16 changes: 13 additions & 3 deletions src/plugin/adapter-registry/c-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { DebugProtocol } from '@vscode/debugprotocol';
import * as vscode from 'vscode';
import { sendRequest } from '../../common/debug-requests';
import { toHexStringWithRadixMarker, VariableRange } from '../../common/memory-range';
import { AdapterVariableTracker, extractAddress, notADigit } from './adapter-capabilities';
import { AdapterVariableTracker, decimalAddress, extractAddress, hexAddress, notADigit } from './adapter-capabilities';

export namespace CEvaluateExpression {
export function sizeOf(expression: string): string {
Expand Down Expand Up @@ -47,7 +47,7 @@ export class CTracker extends AdapterVariableTracker {
const evaluateName = variable.evaluateName ?? variable.name;
[variableAddress, variableSize] = await Promise.all([
variableAddress ?? this.getAddressOfVariable(evaluateName, session),
this.getSizeOfVariable(evaluateName, session)
this.getSizeOfVariable(evaluateName, session),
]);
} catch (err) {
this.logger.warn('Unable to resolve location and size of', variable.name + (err instanceof Error ? ':\n\t' + err.message : ''));
Expand All @@ -58,12 +58,15 @@ export class CTracker extends AdapterVariableTracker {
}
this.logger.debug('Resolved', variable.name, { start: variableAddress, size: variableSize });
const address = BigInt(variableAddress);
const startAddress = toHexStringWithRadixMarker(address);
const isPointer = this.isMaybePointer(variable, startAddress);
const variableRange: VariableRange = {
name: variable.name,
startAddress: toHexStringWithRadixMarker(address),
startAddress,
endAddress: variableSize === undefined ? undefined : toHexStringWithRadixMarker(address + variableSize),
value: variable.value,
type: variable.type,
isPointer,
};
return variableRange;
}
Expand All @@ -77,4 +80,11 @@ export class CTracker extends AdapterVariableTracker {
const response = await sendRequest(session, 'evaluate', { expression: CEvaluateExpression.sizeOf(variableName), context: 'watch', frameId: this.currentFrame });
return notADigit.test(response.result) ? undefined : BigInt(response.result);
}

protected isMaybePointer({ value, type }: DebugProtocol.Variable, startAddress: string): boolean {
if (type?.endsWith('*')) { return true; } // Definitely a pointer

// Might reasonably get cast as a pointer
return (value !== startAddress) && (hexAddress.test(value) || decimalAddress.test(value));
}
}
8 changes: 7 additions & 1 deletion src/plugin/memory-webview-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
WriteMemoryResult,
writeMemoryType,
} from '../common/messaging';
import { getVisibleColumns, WebviewContext } from '../common/webview-context';
import { getVisibleColumns, isWebviewVariableContext, WebviewContext } from '../common/webview-context';
import { AddressPaddingOptions, MemoryViewSettings, ScrollingBehavior } from '../webview/utils/view-types';
import { isVariablesContext } from './external-views';
import { outputChannelLogger } from './logger';
Expand All @@ -65,6 +65,7 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider {
public static ViewType = `${manifest.PACKAGE_NAME}.memory`;
public static ShowCommandType = `${manifest.PACKAGE_NAME}.show`;
public static VariableCommandType = `${manifest.PACKAGE_NAME}.show-variable`;
public static GoToValueCommandType = `${manifest.PACKAGE_NAME}.go-to-value`;
public static ToggleAsciiColumnCommandType = `${manifest.PACKAGE_NAME}.toggle-ascii-column`;
public static ToggleVariablesColumnCommandType = `${manifest.PACKAGE_NAME}.toggle-variables-column`;
public static ToggleRadixPrefixCommandType = `${manifest.PACKAGE_NAME}.toggle-radix-prefix`;
Expand Down Expand Up @@ -97,6 +98,11 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider {
this.show({ memoryReference });
}
}),
vscode.commands.registerCommand(MemoryWebview.GoToValueCommandType, async args => {
if (isWebviewVariableContext(args) && args.variable.isPointer) {
this.show({ memoryReference: args.variable.value });
}
}),
vscode.commands.registerCommand(MemoryWebview.ToggleVariablesColumnCommandType, (ctx: WebviewContext) => {
this.toggleWebviewColumn(ctx, 'variables');
}),
Expand Down
23 changes: 20 additions & 3 deletions src/webview/utils/vscode-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,24 @@ export interface VscodeContext {
export type WebviewSection = 'optionsWidget' | 'advancedOptionsOverlay' | 'memoryTable';

export function createVscodeContext<C extends {}>(context: C): VscodeContext {
return { 'data-vscode-context': JSON.stringify(context) };
return { 'data-vscode-context': JSON.stringify(includeFlatKeys(context)) };
}

function includeFlatKeys(src: object): Record<string, unknown> {
return { ...src, ...flattenKeys(src) };
}

// VSCode context cannot make use of nested keys in 'when' clauses.
function flattenKeys(src: object, dst: Record<string, unknown> = {}, prefix = 'memory-inspector.'): Record<string, unknown> {
if (!src || typeof src !== 'object') { return dst; }
for (const [key, value] of Object.entries(src)) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
flattenKeys(value, dst, `${prefix}${key}.`);
} else {
dst[`${prefix}${key}`] = value;
}
}
return dst;
}

export function createSectionVscodeContext(webviewSection: WebviewSection): VscodeContext {
Expand All @@ -44,7 +61,7 @@ export function createAppVscodeContext(context: Omit<WebviewContext, 'webviewSec
}

export function createVariableVscodeContext(variable: BigIntVariableRange): VscodeContext {
const { name, type, value } = variable;
return createVscodeContext({ variable: { name, type, value } });
const { name, type, value, isPointer } = variable;
return createVscodeContext({ variable: { name, type, value, isPointer } });
}

0 comments on commit 36db026

Please sign in to comment.