Skip to content

Commit

Permalink
Inital pass at updated VariablesResponse to support vertial variables
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdp committed Nov 14, 2024
1 parent d085740 commit 6add727
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 1 deletion.
208 changes: 208 additions & 0 deletions src/debugProtocol/events/responses/VariablesResponse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ describe('VariablesResponse', () => {
refCount: 2,
isConst: false,
isContainer: true,
isVirtual: false,
type: VariableType.AssociativeArray,
keyType: 'String',
value: undefined,
Expand All @@ -173,13 +174,15 @@ describe('VariablesResponse', () => {
refCount: 1,
value: 'Bob',
type: VariableType.String,
isVirtual: false,
isContainer: false,
isConst: false
}, {
name: 'lastName',
refCount: 1,
value: undefined,
isContainer: false,
isVirtual: false,
type: VariableType.Invalid,
isConst: false
}]
Expand All @@ -203,13 +206,15 @@ describe('VariablesResponse', () => {
refCount: 2, // 4 bytes
isConst: false, // 0 bytes -- part of flags
isContainer: true, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.AssociativeArray, // 1 byte
keyType: 'String', // 1 byte
// element_count // 4 bytes
children: [{
// flags // 1 byte
isContainer: false, // 0 bytes --part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.String, // 1 byte
name: 'firstName', // 10 bytes
refCount: 1, // 4 bytes
Expand All @@ -218,6 +223,7 @@ describe('VariablesResponse', () => {
// flags // 1 byte
isContainer: false, // 0 bytes -- part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.Invalid, // 1 byte
name: 'lastName', // 9 bytes
refCount: 1 // 4 bytes
Expand All @@ -226,6 +232,202 @@ describe('VariablesResponse', () => {
});
});

it('handles parent var with children and virtual variables', () => {
let response = VariablesResponse.fromJson({
requestId: 2,
variables: [{
name: 'person',
refCount: 2,
isConst: false,
isContainer: true,
type: VariableType.AssociativeArray,
keyType: VariableType.String,
value: undefined,
children: [{
name: 'firstName',
refCount: 1,
value: 'Bob',
type: VariableType.String,
isContainer: false,
isConst: false
}, {
name: 'lastName',
refCount: 1,
value: undefined,
isContainer: false,
type: VariableType.Invalid,
isConst: false
}, {
name: '$children',
refCount: 1,
value: undefined,
isContainer: true,
isConst: false,
isVirtual: true,
childCount: 4,
keyType: VariableType.Integer,
type: VariableType.Array
}, {
name: '$parent',
refCount: 1,
value: undefined,
isContainer: true,
isConst: false,
isVirtual: true,
childCount: 4,
keyType: VariableType.String,
type: VariableType.SubtypedObject
}, {
name: '$threadinfo',
refCount: 1,
value: undefined,
isContainer: true,
isConst: false,
isVirtual: true,
childCount: 4,
keyType: VariableType.String,
type: VariableType.AssociativeArray
}]
}]
});

expect(response.data).to.eql({
packetLength: undefined,
errorCode: ErrorCode.OK,
requestId: 2,
variables: [{
name: 'person',
refCount: 2,
isConst: false,
isContainer: true,
isVirtual: false,
type: VariableType.AssociativeArray,
keyType: 'String',
value: undefined,
children: [{
name: 'firstName',
refCount: 1,
value: 'Bob',
type: VariableType.String,
isVirtual: false,
isContainer: false,
isConst: false
}, {
name: 'lastName',
refCount: 1,
value: undefined,
isContainer: false,
isVirtual: false,
type: VariableType.Invalid,
isConst: false
}, {
name: '$children',
refCount: 1,
value: undefined,
isContainer: true,
isVirtual: true,
isConst: false,
childCount: 4,
type: VariableType.Array,
keyType: VariableType.Integer
}, {
name: '$parent',
refCount: 1,
value: undefined,
isContainer: true,
isVirtual: true,
isConst: false,
childCount: 4,
type: VariableType.SubtypedObject,
keyType: VariableType.String
}, {
name: '$threadinfo',
refCount: 1,
value: undefined,
isContainer: true,
isVirtual: true,
isConst: false,
childCount: 4,
type: VariableType.AssociativeArray,
keyType: VariableType.String
}]
}]
});

response = VariablesResponse.fromBuffer(response.toBuffer());

expect(response.success).to.be.true;


expect(
response.data
).to.eql({
packetLength: 132, // 4 bytes
errorCode: ErrorCode.OK, // 4 bytes
requestId: 2, // 4 bytes
// num_variables // 4 bytes
variables: [{
// flags // 1 byte
name: 'person', // 7 bytes
refCount: 2, // 4 bytes
isConst: false, // 0 bytes -- part of flags
isContainer: true, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.AssociativeArray, // 1 byte
keyType: 'String', // 1 byte
// element_count // 4 bytes
children: [{
// flags // 1 byte
isContainer: false, // 0 bytes --part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.String, // 1 byte
name: 'firstName', // 10 bytes
refCount: 1, // 4 bytes
value: 'Bob' // 4 bytes
}, {
// flags // 1 byte
isContainer: false, // 0 bytes -- part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.Invalid, // 1 byte
name: 'lastName', // 9 bytes
refCount: 1 // 4 bytes
}, {
// flags // 1 byte
isContainer: true, // 0 bytes -- part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: true, // 0 bytes -- part of flags
childCount: 4, // 4 bytes
type: VariableType.Array, // 1 byte
keyType: VariableType.Integer, // 1 byte
name: '$children', // 10 bytes
refCount: 1 // 4 bytes
}, {
// flags // 1 byte
isContainer: true, // 0 bytes -- part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: true, // 0 bytes -- part of flags
childCount: 4, // 4 bytes
type: VariableType.SubtypedObject, // 1 byte
keyType: VariableType.String, // 1 byte
name: '$parent', // 8 bytes
refCount: 1 // 4 bytes
}, {
// flags // 1 byte
isContainer: true, // 0 bytes -- part of flags
isConst: false, // 0 bytes -- part of flags
isVirtual: true, // 0 bytes -- part of flags
childCount: 4, // 4 bytes
type: VariableType.AssociativeArray, // 1 byte
keyType: VariableType.String, // 1 byte
name: '$threadinfo', // 12 bytes
refCount: 1 // 4 bytes
}]
}]
});
});

it('handles every variable type', () => {
let response = VariablesResponse.fromBuffer(
VariablesResponse.fromJson({
Expand Down Expand Up @@ -387,6 +589,7 @@ describe('VariablesResponse', () => {
variables: [{
isConst: false,
isContainer: true,
isVirtual: false,
type: VariableType.AssociativeArray,
name: 'm',
refCount: 2,
Expand All @@ -396,6 +599,7 @@ describe('VariablesResponse', () => {
}, {
isConst: false,
isContainer: true,
isVirtual: false,
type: VariableType.Array,
name: 'nodes',
refCount: 2,
Expand All @@ -405,6 +609,7 @@ describe('VariablesResponse', () => {
}, {
isConst: false,
isContainer: false,
isVirtual: false,
type: VariableType.String,
name: 'message',
refCount: 2,
Expand All @@ -427,6 +632,7 @@ describe('VariablesResponse', () => {
// flags // 1 byte
isConst: false, // 0 bytes -- part of flags
isContainer: true, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.AssociativeArray, // 1 byte
name: 'm', // 2 bytes
refCount: 2, // 4 bytes
Expand All @@ -436,6 +642,7 @@ describe('VariablesResponse', () => {
// flags // 1 byte
isConst: false, // 0 bytes -- part of flags
isContainer: true, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.Array, // 1 byte
name: 'nodes', // 6 bytes
refCount: 2, // 4 bytes
Expand All @@ -445,6 +652,7 @@ describe('VariablesResponse', () => {
// flags // 1 byte
isConst: false, // 0 bytes -- part of flags
isContainer: false, // 0 bytes -- part of flags
isVirtual: false, // 0 bytes -- part of flags
type: VariableType.String, // 1 byte
name: 'message', // 8 bytes
refCount: 2, // 4 bytes
Expand Down
14 changes: 13 additions & 1 deletion src/debugProtocol/events/responses/VariablesResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class VariablesResponse implements ProtocolResponse {
if (variable.isContainer && util.isNullish(variable.childCount) && !hasChildrenArray) {
throw new Error('Container variable must have one of these properties defined: childCount, children');
}
variable.isVirtual ??= false;
}
return response;
}
Expand Down Expand Up @@ -81,6 +82,7 @@ export class VariablesResponse implements ProtocolResponse {
const isNameHere = (flags & VariableFlags.isNameHere) > 0;
const isRefCounted = (flags & VariableFlags.isRefCounted) > 0;
const isValueHere = (flags & VariableFlags.isValueHere) > 0;
variable.isVirtual = (flags & VariableFlags.isVirtual) > 0;
const variableTypeCode = smartBuffer.readUInt8();
variable.type = VariableTypeCode[variableTypeCode] as VariableType; // variable_type

Expand Down Expand Up @@ -185,6 +187,7 @@ export class VariablesResponse implements ProtocolResponse {
flags |= Array.isArray(variable.children) ? 0 : VariableFlags.isChildKey;
flags |= variable.isConst ? VariableFlags.isConst : 0;
flags |= variable.isContainer ? VariableFlags.isContainer : 0;
flags |= variable.isVirtual ? VariableFlags.isVirtual : 0;

const isNameHere = !util.isNullish(variable.name);
flags |= isNameHere ? VariableFlags.isNameHere : 0;
Expand Down Expand Up @@ -312,7 +315,12 @@ export enum VariableFlags {
* Value is container, key lookup is case sensitive
* @since protocol 3.1.0
*/
isKeysCaseSensitive = 64
isKeysCaseSensitive = 64,
/**
* Indicates whether the associated variable is virtual or not.
* @since protocol 3.3.0
*/
isVirtual = 128
}

/**
Expand Down Expand Up @@ -391,6 +399,10 @@ export interface Variable {
* Is this variable a container var (i.e. an array or object with children)
*/
isContainer: boolean;
/**
* Indicates whether the associated variable is virtual or not.
*/
isVirtual?: boolean;
/**
* If the variable is a container, it will have child elements. this is the number of those children. If `.children` is set, this field will be set to undefined
* (meaning it will be ignored during serialization)
Expand Down

0 comments on commit 6add727

Please sign in to comment.