Skip to content

Commit

Permalink
[trace-view] Added support for tracking struct/enum values (#19724)
Browse files Browse the repository at this point in the history
## Description 

This PR adds support for tracking variable values for structs and enums.
It also does some code cleanup (mostly via renamings)

## Test plan 

Tested manually
  • Loading branch information
awelc authored Oct 10, 2024
1 parent b976380 commit 7d006ef
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 107 deletions.
106 changes: 81 additions & 25 deletions external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import {
} from '@vscode/debugadapter';
import { DebugProtocol } from '@vscode/debugprotocol';
import * as path from 'path';
import { Runtime, RuntimeEvents, IRuntimeVariableScope } from './runtime';
import {
Runtime,
RuntimeEvents,
RuntimeValueType,
IRuntimeVariableScope,
CompoundType
} from './runtime';
import { run } from 'node:test';

const enum LogLevel {
Log = 'log',
Expand Down Expand Up @@ -80,19 +87,16 @@ export class MoveDebugSession extends LoggingDebugSession {
private runtime: Runtime;

/**
* Handles to create variable scopes
* (ideally we would use numbers but DAP package does not like it)
*
* Handles to create variable scopes and compound variable values.
*/
private variableHandles: Handles<IRuntimeVariableScope>;

private variableHandles: Handles<IRuntimeVariableScope | CompoundType>;

public constructor() {
super();
this.setDebuggerLinesStartAt1(false);
this.setDebuggerColumnsStartAt1(false);
this.runtime = new Runtime();
this.variableHandles = new Handles<IRuntimeVariableScope>();
this.variableHandles = new Handles<IRuntimeVariableScope | CompoundType>();

// setup event handlers

Expand Down Expand Up @@ -268,41 +272,94 @@ export class MoveDebugSession extends LoggingDebugSession {
this.sendResponse(response);
}

/**
* Converts a runtime value to a DAP variable.
*
* @param value variable value
* @param name variable name
* @param type optional variable type
* @returns a DAP variable.
*/
private convertRuntimeValue(
value: RuntimeValueType,
name: string,
type?: string
): DebugProtocol.Variable {
if (typeof value === 'string') {
return {
name,
type,
value,
variablesReference: 0
};
} else if (Array.isArray(value)) {
const compoundValueReference = this.variableHandles.create(value);
return {
name,
type,
value: '(' + value.length + ')[...]',
variablesReference: compoundValueReference
};
} else {
const compoundValueReference = this.variableHandles.create(value);
const accessChainParts = value.type.split('::');
const datatypeName = accessChainParts[accessChainParts.length - 1];
return {
name,
type: value.variantName
? value.type + '::' + value.variantName
: value.type,
value: (value.variantName
? datatypeName + '::' + value.variantName
: datatypeName
) + '{...}',
variablesReference: compoundValueReference
};
}
}

/**
* Converts runtime variables to DAP variables.
*
* @param runtimeScope runtime variables scope,
* @returns an array of variables.
* @returns an array of DAP variables.
*/
private convertRuntimeVariables(runtimeScope: IRuntimeVariableScope): DebugProtocol.Variable[] {
const variables: DebugProtocol.Variable[] = [];
const runtimeVariables = runtimeScope.locals;
for (let i = 0; i < runtimeVariables.length; i++) {
const v = runtimeVariables[i];
runtimeVariables.forEach(v => {
if (v) {
variables.push({
name: v.name,
type: v.type,
value: v.value,
variablesReference: 0
});
variables.push(this.convertRuntimeValue(v.value, v.name, v.type));
}
}

});
return variables;
}

protected variablesRequest(
response: DebugProtocol.VariablesResponse,
args: DebugProtocol.VariablesArguments
): void {
const handle = this.variableHandles.get(args.variablesReference);
if (!handle) {
this.sendResponse(response);
return;
}
try {
const variables = this.convertRuntimeVariables(handle);
const variableHandle = this.variableHandles.get(args.variablesReference);
let variables: DebugProtocol.Variable[] = [];
if (variableHandle) {
if ('locals' in variableHandle) {
// we are dealing with a sccope
variables = this.convertRuntimeVariables(variableHandle);
} else {
// we are dealing with a compound value
if (Array.isArray(variableHandle)) {
for (let i = 0; i < variableHandle.length; i++) {
const v = variableHandle[i];
variables.push(this.convertRuntimeValue(v, String(i)));
}
} else {
variableHandle.fields.forEach(([fname, fvalue]) => {
variables.push(this.convertRuntimeValue(fvalue, fname));
});
}
}
}
if (variables.length > 0) {
response.body = {
variables
Expand All @@ -312,7 +369,6 @@ export class MoveDebugSession extends LoggingDebugSession {
response.success = false;
response.message = err instanceof Error ? err.message : String(err);
}

this.sendResponse(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,33 @@ export interface IRuntimeVariableScope {
locals: (IRuntimeVariable | undefined)[];
}

/**
* A compound type:
* - a vector (converted to an array of values)
* - a struct/enum (converted to an array of string/field value pairs)
*/
export type CompoundType = RuntimeValueType[] | IRuntimeCompundValue;

/**
* A runtime value can have any of the following types:
* - boolean, number, string (converted to string)
* - compound type (vector, struct, enum)
*/
export type RuntimeValueType = string | CompoundType;

export interface IRuntimeCompundValue {
fields: [string, RuntimeValueType][];
type: string;
variantName?: string;
variantTag?: number;
}

/**
* Describes a runtime local variable.
*/
interface IRuntimeVariable {
name: string;
value: string;
value: RuntimeValueType;
type: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,40 @@ import { ModuleInfo } from './utils';

// Data types corresponding to source map file JSON schema.

interface ISrcDefinitionLocation {
interface JSONSrcDefinitionLocation {
file_hash: number[];
start: number;
end: number;
}

interface ISrcStructSourceMapEntry {
definition_location: ISrcDefinitionLocation;
type_parameters: [string, ISrcDefinitionLocation][];
fields: ISrcDefinitionLocation[];
interface JSONSrcStructSourceMapEntry {
definition_location: JSONSrcDefinitionLocation;
type_parameters: [string, JSONSrcDefinitionLocation][];
fields: JSONSrcDefinitionLocation[];
}

interface ISrcEnumSourceMapEntry {
definition_location: ISrcDefinitionLocation;
type_parameters: [string, ISrcDefinitionLocation][];
variants: [[string, ISrcDefinitionLocation], ISrcDefinitionLocation[]][];
interface JSONSrcEnumSourceMapEntry {
definition_location: JSONSrcDefinitionLocation;
type_parameters: [string, JSONSrcDefinitionLocation][];
variants: [[string, JSONSrcDefinitionLocation], JSONSrcDefinitionLocation[]][];
}

interface ISrcFunctionMapEntry {
definition_location: ISrcDefinitionLocation;
type_parameters: [string, ISrcDefinitionLocation][];
parameters: [string, ISrcDefinitionLocation][];
locals: [string, ISrcDefinitionLocation][];
interface JSONSrcFunctionMapEntry {
definition_location: JSONSrcDefinitionLocation;
type_parameters: [string, JSONSrcDefinitionLocation][];
parameters: [string, JSONSrcDefinitionLocation][];
locals: [string, JSONSrcDefinitionLocation][];
nops: Record<string, any>;
code_map: Record<string, ISrcDefinitionLocation>;
code_map: Record<string, JSONSrcDefinitionLocation>;
is_native: boolean;
}

interface ISrcRootObject {
definition_location: ISrcDefinitionLocation;
interface JSONSrcRootObject {
definition_location: JSONSrcDefinitionLocation;
module_name: string[];
struct_map: Record<string, ISrcStructSourceMapEntry>;
enum_map: Record<string, ISrcEnumSourceMapEntry>;
function_map: Record<string, ISrcFunctionMapEntry>;
struct_map: Record<string, JSONSrcStructSourceMapEntry>;
enum_map: Record<string, JSONSrcEnumSourceMapEntry>;
function_map: Record<string, JSONSrcFunctionMapEntry>;
constant_map: Record<string, string>;
}

Expand Down Expand Up @@ -126,7 +126,7 @@ export function readAllSourceMaps(
* @throws Error if with a descriptive error message if the source map cannot be read.
*/
function readSourceMap(sourceMapPath: string, filesMap: Map<string, IFileInfo>): ISourceMap {
const sourceMapJSON: ISrcRootObject = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8'));
const sourceMapJSON: JSONSrcRootObject = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8'));

const fileHash = Buffer.from(sourceMapJSON.definition_location.file_hash).toString('base64');
const modInfo: ModuleInfo = {
Expand Down Expand Up @@ -212,7 +212,7 @@ function readSourceMap(sourceMapPath: string, filesMap: Map<string, IFileInfo>):
* @param sourceMapLines
*/
function prePopulateSourceMapLines(
sourceMapJSON: ISrcRootObject,
sourceMapJSON: JSONSrcRootObject,
fileInfo: IFileInfo,
sourceMapLines: Set<number>
): void {
Expand Down Expand Up @@ -265,7 +265,7 @@ function prePopulateSourceMapLines(
* @param sourceMapLines set of source file lines.
*/
function addLinesForLocation(
loc: ISrcDefinitionLocation,
loc: JSONSrcDefinitionLocation,
fileInfo: IFileInfo,
sourceMapLines: Set<number>
): void {
Expand Down
Loading

0 comments on commit 7d006ef

Please sign in to comment.