Skip to content

Commit 7522eb8

Browse files
committed
refactor: move type/variable parsing logic from utils.ts to debugger.ts
1 parent e91b696 commit 7522eb8

File tree

5 files changed

+187
-188
lines changed

5 files changed

+187
-188
lines changed

src/configuration.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as path from 'path';
33

44
import * as vars from './variables';
55
import * as utils from './utils';
6+
import * as dbg from './debugger';
67
import { Log as logger } from './logger';
78
import { PghhError } from './error';
89

@@ -94,7 +95,7 @@ function parseConfiguration(contents: unknown): ConfigurationFile | undefined {
9495
typeName = typeName.trim();
9596

9697
/* NodeTag used also as type name, so it must be valid identifier */
97-
if (!utils.isValidIdentifier(typeName)) {
98+
if (!dbg.isValidIdentifier(typeName)) {
9899
vscode.window.showErrorMessage(`typeName must be valid identifier. given: ${typeName}`);
99100
return;
100101
}
@@ -115,7 +116,7 @@ function parseConfiguration(contents: unknown): ConfigurationFile | undefined {
115116
}
116117

117118
memberName = memberName.trim();
118-
if (!utils.isValidIdentifier(memberName)) {
119+
if (!dbg.isValidIdentifier(memberName)) {
119120
vscode.window.showErrorMessage(`memberName field ${memberName} is not valid identifier`);
120121
return;
121122
}
@@ -510,7 +511,7 @@ function parseConfiguration(contents: unknown): ConfigurationFile | undefined {
510511
}
511512

512513
o = o.trim();
513-
if (!utils.isValidIdentifier(o)) {
514+
if (!dbg.isValidIdentifier(o)) {
514515
continue;
515516
}
516517

src/debugger.ts

Lines changed: 150 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,156 @@ const builtInTypes = new Set([
3030
/* src/include/nodes/nodes.h */
3131
'Cost', 'Selectivity', 'Cardinality', 'ParseLoc', 'NodeTag',
3232
]);
33+
const identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
34+
35+
/**
36+
* Check that given string represents valid C identifier.
37+
* Identifier can represent struct fields, type names, variable names etc...
38+
*
39+
* @param value String to test
40+
* @returns true if string represents valid C identifier
41+
*/
42+
export function isValidIdentifier(value: string) {
43+
return identifierRegex.test(value);
44+
}
45+
46+
export function getStructNameFromType(type: string) {
47+
/* [const] [struct] NAME [*]+ */
48+
/*
49+
* Start locating from end, because we can use '*' as the boundary of
50+
* typename end.
51+
*
52+
* During some manual testing observed common behavior of debuggers:
53+
* after type name can be only pointer - that is no qualifiers will follow.
54+
*
55+
* i.e. declared in src -> DAP 'type':
56+
*
57+
* PlannerInfo const * -> const PlannerInfo *
58+
* int volatile * const -> volatile int * const
59+
* int const * const -> const int * const;
60+
* const Relids -> const Relids
61+
*
62+
* XXX: this is broken for FLA (they have [] at the end), but they
63+
* don't get here yet, so don't worry.
64+
*/
65+
const lastPtrIndex = type.indexOf('*');
66+
let endOfIdentifier;
67+
if (lastPtrIndex === -1) {
68+
/* Type without any pointer */
69+
endOfIdentifier = type.length;
70+
} else {
71+
endOfIdentifier = lastPtrIndex - 1;
72+
while (endOfIdentifier >= 0 && type.charAt(endOfIdentifier) === ' ') {
73+
endOfIdentifier--;
74+
continue;
75+
}
76+
77+
/*
78+
* Another observation is that all debuggers add spaces around pointers,
79+
* so one might think we can omit such check. But do not forget that
80+
* we are working with *effective* types - after we have substituted
81+
* aliased typename and user can omit spaces in between.
82+
*/
83+
if (endOfIdentifier < 0) {
84+
endOfIdentifier = lastPtrIndex;
85+
}
86+
}
87+
88+
/* Search for start of typename - it must be first space before typename */
89+
let startOfIndentifier = type.lastIndexOf(' ', endOfIdentifier);
90+
if (startOfIndentifier === -1) {
91+
/* Type without any qualifiers */
92+
startOfIndentifier = 0;
93+
} else {
94+
startOfIndentifier++;
95+
}
96+
97+
return type.substring(startOfIndentifier, endOfIdentifier + 1);
98+
}
99+
100+
/**
101+
* Substitute struct name from type to provided struct name.
102+
* This takes qualifiers into account (const, volatile, *, etc...)
103+
*
104+
* @param type Whole type name of original variable (including qualifiers)
105+
* @param target The name of the type (or base type) to be substituted
106+
* @returns Result type name
107+
*/
108+
export function substituteStructName(type: string, target: string) {
109+
const typename = getStructNameFromType(type);
110+
return type.replace(typename, target);}
111+
112+
/*
113+
* Check that 'type' contains exact count of pointers in it
114+
*/
115+
export function havePointersCount(type: string, count: number) {
116+
const firstIndex = type.indexOf('*');
117+
118+
/* For now only 0 and 1 will be used, so add specialized codepath */
119+
if (count === 0) {
120+
return firstIndex === -1;
121+
}
122+
if (count === 1) {
123+
return firstIndex !== -1 && firstIndex === type.lastIndexOf('*');
124+
}
125+
126+
let result = 1;
127+
let index = firstIndex;
128+
while ((index = type.indexOf('*', index + 1)) !== -1) {
129+
++result;
130+
}
131+
132+
return result === count;
133+
}
134+
135+
/**
136+
* Check that type represent either value struct or pointer type, i.e.
137+
* it is not array type. Roughly speaking, type contains at most 1 pointer.
138+
*
139+
* @param type Type specifier
140+
* @returns Type represents plain value struct or pointer type
141+
*/
142+
export function isValueStructOrPointerType(type: string) {
143+
const firstPointerPos = type.indexOf('*');
144+
if (firstPointerPos === -1) {
145+
/* Value struct */
146+
return true;
147+
}
148+
149+
const secondPointerPos = type.indexOf('*', firstPointerPos + 1);
150+
if (secondPointerPos === -1) {
151+
/* Pointer type, not array */
152+
return true;
153+
}
154+
155+
return false;
156+
}
157+
158+
/**
159+
* Check that output from evaluation is correct enum value.
160+
* That is it is not error message, pointer or something else.
161+
* So, 'result' looks like real enum value.
162+
*
163+
* @returns 'true' if looks like enum value, 'false' otherwise
164+
*/
165+
export function isEnumResult(result: string) {
166+
return isValidIdentifier(result);
167+
}
168+
169+
export function isFlexibleArrayMember(type: string) {
170+
return type.endsWith('[]');
171+
}
172+
173+
/**
174+
* Shortcut function to test that pointer is NULL.
175+
* Used for situations, where only pointer value is present, without variable.....
176+
*
177+
* @param pointer Pointer value in HEX form
178+
* @returns true if pointer value is NULL
179+
*/
180+
export function pointerIsNull(pointer: string) {
181+
return pointer === '0x0' || /0x0+/.test(pointer);
182+
}
33183

34184
export enum DebuggerType {
35185
CppDbg,
@@ -950,17 +1100,6 @@ export class CodeLLDBDebuggerFacade extends GenericDebuggerFacade {
9501100
}
9511101
}
9521102

953-
/**
954-
* Shortcut function to test that pointer is NULL.
955-
* Used for situations, where only pointer value is present, without variable.....
956-
*
957-
* @param pointer Pointer value in HEX form
958-
* @returns true if pointer value is NULL
959-
*/
960-
export function pointerIsNull(pointer: string) {
961-
return pointer === '0x0' || /0x0+/.test(pointer);
962-
}
963-
9641103
export function setupDebugger(context: vscode.ExtensionContext,
9651104
variablesView: PgVariablesViewProvider) {
9661105
if (!Features.debugFocusEnabled()) {

src/test/suite/unit.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as assert from 'assert';
22

3-
import * as utils from '../../utils';
3+
import * as dbg from '../../debugger';
44

55
suite('Unit', async function () {
66
test('getStructNameFromType', function () {
@@ -30,7 +30,7 @@ suite('Unit', async function () {
3030
];
3131

3232
for (const [type, expected] of data) {
33-
const actual = utils.getStructNameFromType(type);
33+
const actual = dbg.getStructNameFromType(type);
3434
assert.equal(actual, expected, type);
3535
}
3636
});
@@ -46,7 +46,7 @@ suite('Unit', async function () {
4646
['MemoryContext *', 'MemoryContextData *', 'MemoryContextData * *'],
4747
];
4848
for (const [type, substitution, expected] of data) {
49-
const actual = utils.substituteStructName(type, substitution);
49+
const actual = dbg.substituteStructName(type, substitution);
5050
assert.equal(actual, expected, `${type}: ${substitution}`);
5151
}
5252
});
@@ -70,7 +70,7 @@ suite('Unit', async function () {
7070
['List **', 3, false],
7171
];
7272
for (const [type, count, expected] of data) {
73-
const actual = utils.havePointersCount(type, count);
73+
const actual = dbg.havePointersCount(type, count);
7474
assert.equal(actual, expected, `${type}: ${count}`);
7575
}
7676
});

src/utils.ts

Lines changed: 0 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -7,146 +7,6 @@ import { PghhError } from './error';
77
import * as crypto from 'crypto';
88
import { VsCodeSettings } from './configuration';
99

10-
const identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
11-
12-
/**
13-
* Check that given string represents valid C identifier.
14-
* Identifier can represent struct fields, type names, variable names etc...
15-
*
16-
* @param value String to test
17-
* @returns true if string represents valid C identifier
18-
*/
19-
export function isValidIdentifier(value: string) {
20-
return identifierRegex.test(value);
21-
}
22-
23-
export function getStructNameFromType(type: string) {
24-
/* [const] [struct] NAME [*]+ */
25-
/*
26-
* Start locating from end, because we can use '*' as the boundary of
27-
* typename end.
28-
*
29-
* During some manual testing observed common behavior of debuggers:
30-
* after type name can be only pointer - that is no qualifiers will follow.
31-
*
32-
* i.e. declared in src -> DAP 'type':
33-
*
34-
* PlannerInfo const * -> const PlannerInfo *
35-
* int volatile * const -> volatile int * const
36-
* int const * const -> const int * const;
37-
* const Relids -> const Relids
38-
*
39-
* XXX: this is broken for FLA (they have [] at the end), but they
40-
* don't get here yet, so don't worry.
41-
*/
42-
const lastPtrIndex = type.indexOf('*');
43-
let endOfIdentifier;
44-
if (lastPtrIndex === -1) {
45-
/* Type without any pointer */
46-
endOfIdentifier = type.length;
47-
} else {
48-
endOfIdentifier = lastPtrIndex - 1;
49-
while (endOfIdentifier >= 0 && type.charAt(endOfIdentifier) === ' ') {
50-
endOfIdentifier--;
51-
continue;
52-
}
53-
54-
/*
55-
* Another observation is that all debuggers add spaces around pointers,
56-
* so one might think we can omit such check. But do not forget that
57-
* we are working with *effective* types - after we have substituted
58-
* aliased typename and user can omit spaces in between.
59-
*/
60-
if (endOfIdentifier < 0) {
61-
endOfIdentifier = lastPtrIndex;
62-
}
63-
}
64-
65-
/* Search for start of typename - it must be first space before typename */
66-
let startOfIndentifier = type.lastIndexOf(' ', endOfIdentifier);
67-
if (startOfIndentifier === -1) {
68-
/* Type without any qualifiers */
69-
startOfIndentifier = 0;
70-
} else {
71-
startOfIndentifier++;
72-
}
73-
74-
return type.substring(startOfIndentifier, endOfIdentifier + 1);
75-
}
76-
77-
/**
78-
* Substitute struct name from type to provided struct name.
79-
* This takes qualifiers into account (const, volatile, *, etc...)
80-
*
81-
* @param type Whole type name of original variable (including qualifiers)
82-
* @param target The name of the type (or base type) to be substituted
83-
* @returns Result type name
84-
*/
85-
export function substituteStructName(type: string, target: string) {
86-
const typename = getStructNameFromType(type);
87-
return type.replace(typename, target);}
88-
89-
/*
90-
* Check that 'type' contains exact count of pointers in it
91-
*/
92-
export function havePointersCount(type: string, count: number) {
93-
const firstIndex = type.indexOf('*');
94-
95-
/* For now only 0 and 1 will be used, so add specialized codepath */
96-
if (count === 0) {
97-
return firstIndex === -1;
98-
}
99-
if (count === 1) {
100-
return firstIndex !== -1 && firstIndex === type.lastIndexOf('*');
101-
}
102-
103-
let result = 1;
104-
let index = firstIndex;
105-
while ((index = type.indexOf('*', index + 1)) !== -1) {
106-
++result;
107-
}
108-
109-
return result === count;
110-
}
111-
112-
/**
113-
* Check that type represent either value struct or pointer type, i.e.
114-
* it is not array type. Roughly speaking, type contains at most 1 pointer.
115-
*
116-
* @param type Type specifier
117-
* @returns Type represents plain value struct or pointer type
118-
*/
119-
export function isValueStructOrPointerType(type: string) {
120-
const firstPointerPos = type.indexOf('*');
121-
if (firstPointerPos === -1) {
122-
/* Value struct */
123-
return true;
124-
}
125-
126-
const secondPointerPos = type.indexOf('*', firstPointerPos + 1);
127-
if (secondPointerPos === -1) {
128-
/* Pointer type, not array */
129-
return true;
130-
}
131-
132-
return false;
133-
}
134-
135-
/**
136-
* Check that output from evaluation is correct enum value.
137-
* That is it is not error message, pointer or something else.
138-
* So, 'result' looks like real enum value.
139-
*
140-
* @returns 'true' if looks like enum value, 'false' otherwise
141-
*/
142-
export function isEnumResult(result: string) {
143-
return isValidIdentifier(result);
144-
}
145-
146-
export function isFlexibleArrayMember(type: string) {
147-
return type.endsWith('[]');
148-
}
149-
15010
export function joinPath(base: vscode.Uri, ...paths: string[]) {
15111
return vscode.Uri.joinPath(base, ...paths);
15212
}

0 commit comments

Comments
 (0)