diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts index 093b028720762..1d1a9a5c3cbd6 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/adapter.ts @@ -320,8 +320,8 @@ export class MoveDebugSession extends LoggingDebugSession { + ' when converting ref value for frame id ' + frameID); } - - return this.convertRuntimeValue(local.value, name, type); + const indexPath = [...value.loc.indexPath]; + return this.convertRuntimeValue(local.value, name, indexPath, type); } /** @@ -329,15 +329,22 @@ export class MoveDebugSession extends LoggingDebugSession { * * @param value variable value * @param name variable name + * @param indexPath a path to actual value for compound types (e.g, [1, 7] means + * first field/vector element and then seventh field/vector element) * @param type optional variable type * @returns a DAP variable. + * @throws Error with a descriptive error message if conversion has failed. */ private convertRuntimeValue( value: RuntimeValueType, name: string, + indexPath: number[], type?: string ): DebugProtocol.Variable { if (typeof value === 'string') { + if (indexPath.length > 0) { + throw new Error('Cannot index into a string'); + } return { name, type, @@ -345,6 +352,13 @@ export class MoveDebugSession extends LoggingDebugSession { variablesReference: 0 }; } else if (Array.isArray(value)) { + if (indexPath.length > 0) { + const index = indexPath.pop(); + if (index === undefined || index >= value.length) { + throw new Error('Index path for an array is invalid'); + } + return this.convertRuntimeValue(value[index], name, indexPath, type); + } const compoundValueReference = this.variableHandles.create(value); return { name, @@ -353,6 +367,13 @@ export class MoveDebugSession extends LoggingDebugSession { variablesReference: compoundValueReference }; } else if ('fields' in value) { + if (indexPath.length > 0) { + const index = indexPath.pop(); + if (index === undefined || index >= value.fields.length) { + throw new Error('Index path for a compound type is invalid'); + } + return this.convertRuntimeValue(value.fields[index][1], name, indexPath, type); + } const compoundValueReference = this.variableHandles.create(value); // use type if available as it will have information about whether // it's a reference or not (e.g., `&mut 0x42::mod::SomeStruct`), @@ -373,6 +394,9 @@ export class MoveDebugSession extends LoggingDebugSession { variablesReference: compoundValueReference }; } else { + if (indexPath.length > 0) { + throw new Error('Cannot index into a reference value'); + } return this.convertRefValue(value, name, type); } } @@ -388,7 +412,7 @@ export class MoveDebugSession extends LoggingDebugSession { const runtimeVariables = runtimeScope.locals; runtimeVariables.forEach(v => { if (v) { - variables.push(this.convertRuntimeValue(v.value, v.name, v.type)); + variables.push(this.convertRuntimeValue(v.value, v.name, [], v.type)); } }); return variables; @@ -410,11 +434,11 @@ export class MoveDebugSession extends LoggingDebugSession { if (Array.isArray(variableHandle)) { for (let i = 0; i < variableHandle.length; i++) { const v = variableHandle[i]; - variables.push(this.convertRuntimeValue(v, String(i))); + variables.push(this.convertRuntimeValue(v, String(i), [])); } } else { variableHandle.fields.forEach(([fname, fvalue]) => { - variables.push(this.convertRuntimeValue(fvalue, fname)); + variables.push(this.convertRuntimeValue(fvalue, fname, [])); }); } } diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts index 187adb8dfe2f6..a1b3e69ce63df 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/runtime.ts @@ -43,6 +43,7 @@ export type RuntimeValueType = string | CompoundType | IRuntimeRefValue; export interface IRuntimeVariableLoc { frameID: number; localIndex: number; + indexPath: number[]; } /** @@ -789,7 +790,7 @@ export class Runtime extends EventEmitter { * @returns string representation of the variable. */ private varToString(tabs: string, variable: IRuntimeVariable): string { - return this.valueToString(tabs, variable.value, variable.name, variable.type); + return this.valueToString(tabs, variable.value, variable.name, [], variable.type); } /** @@ -804,7 +805,7 @@ export class Runtime extends EventEmitter { : compoundValue.type; let res = '(' + type + ') {\n'; for (const [name, value] of compoundValue.fields) { - res += this.valueToString(tabs + this.singleTab, value, name); + res += this.valueToString(tabs + this.singleTab, value, name, []); } res += tabs + '}\n'; return res; @@ -839,7 +840,8 @@ export class Runtime extends EventEmitter { if (!local) { return res; } - return this.valueToString(tabs, local.value, name, type); + const indexPath = [...refValue.loc.indexPath]; + return this.valueToString(tabs, local.value, name, indexPath, type); } /** @@ -847,6 +849,8 @@ export class Runtime extends EventEmitter { * * @param value runtime value. * @param name name of the variable containing the value. + * @param indexPath a path to actual value for compound types (e.g, [1, 7] means + * first field/vector element and then seventh field/vector element) * @param type optional type of the variable containing the value. * @returns string representation of the value. */ @@ -854,6 +858,7 @@ export class Runtime extends EventEmitter { tabs: string, value: RuntimeValueType, name: string, + indexPath: number[], type?: string ): string { let res = ''; @@ -863,19 +868,32 @@ export class Runtime extends EventEmitter { res += tabs + 'type: ' + type + '\n'; } } else if (Array.isArray(value)) { - res += tabs + name + ' : [\n'; - for (let i = 0; i < value.length; i++) { - res += this.valueToString(tabs + this.singleTab, value[i], String(i)); - } - res += tabs + ']\n'; - if (type) { - res += tabs + 'type: ' + type + '\n'; + if (indexPath.length > 0) { + const index = indexPath.pop(); + if (index !== undefined) { + res += this.valueToString(tabs, value[index], name, indexPath, type); + } + } else { + res += tabs + name + ' : [\n'; + for (let i = 0; i < value.length; i++) { + res += this.valueToString(tabs + this.singleTab, value[i], String(i), indexPath); + } + res += tabs + ']\n'; + if (type) { + res += tabs + 'type: ' + type + '\n'; + } } - return res; } else if ('fields' in value) { - res += tabs + name + ' : ' + this.compoundValueToString(tabs, value); - if (type) { - res += tabs + 'type: ' + type + '\n'; + if (indexPath.length > 0) { + const index = indexPath.pop(); + if (index !== undefined) { + res += this.valueToString(tabs, value.fields[index][1], name, indexPath, type); + } + } else { + res += tabs + name + ' : ' + this.compoundValueToString(tabs, value); + if (type) { + res += tabs + 'type: ' + type + '\n'; + } } } else { res += this.refValueToString(tabs, value, name, type); diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts b/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts index 2e5e80c08b5b1..dcc25f775c3a9 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts +++ b/external-crates/move/crates/move-analyzer/trace-adapter/src/trace_utils.ts @@ -517,7 +517,7 @@ export function readTrace( // if a local is read or written, set its end of lifetime // to infinite (end of frame) const location = effect.Write ? effect.Write.location : effect.Read!.location; - const loc = processJSONLocalLocation(location, localLifetimeEnds); + const loc = processJSONLocalLocation(location, [], localLifetimeEnds); if (effect.Write) { if (loc !== undefined) { // Process a write only if the location is supported. @@ -788,12 +788,15 @@ function JSONTraceAddressToHexString(address: string): string { * Processes a location of a local variable in a JSON trace: sets the end of its lifetime * when requested and returns its location * @param traceLocation location in the trace. + * @param indexPath a path to actual value for compound types (e.g, [1, 7] means + * first field/vector element and then seventh field/vector element) * @param localLifetimeEnds map of local variable lifetimes (defined if local variable * lifetime should happen). * @returns variable location. */ function processJSONLocalLocation( traceLocation: JSONTraceLocation, + indexPath: number[], localLifetimeEnds?: Map, ): IRuntimeVariableLoc | undefined { if ('Local' in traceLocation) { @@ -804,9 +807,10 @@ function processJSONLocalLocation( lifetimeEnds[localIndex] = FRAME_LIFETIME; localLifetimeEnds.set(frameID, lifetimeEnds); } - return { frameID, localIndex }; + return { frameID, localIndex, indexPath }; } else if ('Indexed' in traceLocation) { - return processJSONLocalLocation(traceLocation.Indexed[0], localLifetimeEnds); + indexPath.push(traceLocation.Indexed[1]); + return processJSONLocalLocation(traceLocation.Indexed[0], indexPath, localLifetimeEnds); } else { // Currently, there is nothing that needs to be done for 'Global' locations, // neither with respect to lifetime nor with respect to location itself. @@ -829,14 +833,14 @@ function processJSONLocalLocation( */ function traceRefValueFromJSON(value: JSONTraceRefValue): RuntimeValueType { if ('MutRef' in value) { - const loc = processJSONLocalLocation(value.MutRef.location); + const loc = processJSONLocalLocation(value.MutRef.location, []); if (!loc) { throw new Error('Unsupported location type in MutRef'); } const ret: IRuntimeRefValue = { mutable: true, loc }; return ret; } else { - const loc = processJSONLocalLocation(value.ImmRef.location); + const loc = processJSONLocalLocation(value.ImmRef.location, []); if (!loc) { throw new Error('Unsupported location type in ImmRef'); } diff --git a/external-crates/move/crates/move-analyzer/trace-adapter/tests/references_deep/test.exp b/external-crates/move/crates/move-analyzer/trace-adapter/tests/references_deep/test.exp index b3d38a0af8046..157bc53c9bd66 100644 --- a/external-crates/move/crates/move-analyzer/trace-adapter/tests/references_deep/test.exp +++ b/external-crates/move/crates/move-analyzer/trace-adapter/tests/references_deep/test.exp @@ -25,14 +25,10 @@ current frame stack: function: bar (m.move:13) scope 0 : - vec_ref : (0x0::m::SomeStruct) { - struct_field : (0x0::m::VecStruct) { - vec_field : [ - 0 : 0 - 1 : 7 - ] - } - } + vec_ref : [ + 0 : 0 + 1 : 7 + ] type: &mut vector current frame stack: @@ -62,13 +58,9 @@ current frame stack: function: bar (m.move:15) scope 0 : - vec_ref : (0x0::m::SomeStruct) { - struct_field : (0x0::m::VecStruct) { - vec_field : [ - 0 : 42 - 1 : 7 - ] - } - } + vec_ref : [ + 0 : 42 + 1 : 7 + ] type: &mut vector