Skip to content

Commit 67f9754

Browse files
authored
Merge pull request #159 from IBM/feature/function_list
Refactor exports to a hard function list
2 parents 4eaf426 + 9f4eb1b commit 67f9754

File tree

12 files changed

+102
-54
lines changed

12 files changed

+102
-54
lines changed

cli/src/targets/index.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export interface ParserError {
2525

2626
export type ParserErrorCallback = (error: ParserError) => void;
2727

28+
export interface ILEFunction {
29+
name: string;
30+
export?: boolean;
31+
lineRange: [number, number];
32+
}
33+
2834
export interface ILEObject {
2935
systemName: string;
3036
longName?: string;
@@ -36,7 +42,7 @@ export interface ILEObject {
3642
reference?: boolean;
3743

3844
/** exported functions */
39-
exports?: string[];
45+
functions?: ILEFunction[];
4046
/** each function import in the object */
4147
imports?: string[];
4248

@@ -483,15 +489,20 @@ export class Targets {
483489
const allSrvPgms = this.getTargetsOfType(`SRVPGM`);
484490
const allModules = this.getTargetsOfType(`MODULE`);
485491

492+
const exportsOf = (theObject: ILEObject) => {
493+
return theObject.functions?.filter(f => f.export).map(f => f.name.toUpperCase()) || [];
494+
}
495+
486496
for (const target of allSrvPgms) {
487-
if (target.exports) {
497+
const targetExports = exportsOf(target);
498+
if (targetExports.length > 0) {
488499
infoOut(`Resolving modules for ${target.systemName}.${target.type}`);
489500

490501
target.deps = [];
491502

492-
for (const exportName of target.exports) {
503+
for (const exportName of targetExports) {
493504
// We loop through each export of the service program and find the module that exports it
494-
const foundModule = allModules.find(mod => mod.exports && mod.exports.includes(exportName.toUpperCase()));
505+
const foundModule = allModules.find(mod => mod.functions && exportsOf(mod).includes(exportName.toUpperCase()));
495506
if (foundModule) {
496507
const alreadyBound = target.deps.some(dep => dep.systemName === foundModule.systemName && dep.type === `MODULE`);
497508
if (!alreadyBound) {
@@ -506,7 +517,7 @@ export class Targets {
506517
this.createOrAppend(this.projectBindingDirectory, target);
507518

508519
// Make sure we can resolve to this service program
509-
for (const e of target.exports) {
520+
for (const e of targetExports) {
510521
this.resolvedExports[e.toUpperCase()] = target;
511522
}
512523
} else {
@@ -534,7 +545,7 @@ export class Targets {
534545
currentTarget.deps = currentTarget.deps.filter(d => ![`SRVPGM`].includes(d.type));
535546

536547
for (const importName of currentTarget.imports) {
537-
if (currentTarget.exports?.includes(importName.toUpperCase())) {
548+
if (exportsOf(currentTarget).includes(importName.toUpperCase())) {
538549
// This happens when a source copy has the prototype and the implementation (export)
539550
continue; // Don't add imports that are also exports
540551
}
@@ -551,7 +562,7 @@ export class Targets {
551562
} else if ([`PGM`, `MODULE`].includes(currentTarget.type)) {
552563
// Perhaps we're looking at a program object, which actually should be a multi
553564
// module program, so we do a lookup for additional modules.
554-
const possibleModuleDep = allModules.find(mod => mod.exports && mod.exports.includes(importName.toUpperCase()));
565+
const possibleModuleDep = allModules.find(mod => mod.functions && exportsOf(mod).includes(importName.toUpperCase()));
555566
if (possibleModuleDep) {
556567
if (!newImports.some(i => i.systemName === possibleModuleDep.systemName && i.type === possibleModuleDep.type)) {
557568
newImports.push(possibleModuleDep);
@@ -639,7 +650,7 @@ export class Targets {
639650
const newModule: ILEObject = {
640651
systemName: currentTarget.systemName,
641652
imports: currentTarget.imports,
642-
exports: [],
653+
functions: [],
643654
headers: currentTarget.headers,
644655
type: `MODULE`,
645656
relativePath: basePath,

cli/src/targets/languages/binder.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export async function binderTargetCallback(targets: Targets, localPath: string,
2121
const target: ILEObjectTarget = {
2222
...ileObject,
2323
deps: [],
24-
exports: []
24+
functions: []
2525
};
2626

2727
if (ileObject.extension === `binder`) {
@@ -58,10 +58,20 @@ export async function binderTargetCallback(targets: Targets, localPath: string,
5858
const symbolTokens = parms[`SYMBOL`];
5959

6060
if (symbolTokens.block && symbolTokens.block.length === 1 && symbolTokens.block[0].type === `string` && symbolTokens.block[0].value) {
61-
target.exports.push(trimQuotes(symbolTokens.block[0].value));
61+
// target.exports.push(trimQuotes(symbolTokens.block[0].value));
62+
target.functions.push({
63+
name: trimQuotes(symbolTokens.block[0].value),
64+
export: true,
65+
lineRange: [0, 0] //TODO: how to get line range?
66+
});
6267
} else
6368
if (symbolTokens.block && symbolTokens.block.length === 1 && symbolTokens.block[0].type === `word` && symbolTokens.block[0].value) {
64-
target.exports.push(trimQuotes(symbolTokens.block[0].value, `"`));
69+
// target.exports.push(trimQuotes(symbolTokens.block[0].value, `"`));
70+
target.functions.push({
71+
name: trimQuotes(symbolTokens.block[0].value, `"`),
72+
export: true,
73+
lineRange: [0, 0] // TODO: how to get line range?
74+
});
6575
} else {
6676
targets.logger.fileLog(ileObject.relativePath, {
6777
message: `Invalid EXPORT found. Single quote string expected.`,
@@ -81,7 +91,10 @@ export async function binderTargetCallback(targets: Targets, localPath: string,
8191
}
8292

8393
// Exports are always uppercase
84-
target.exports = target.exports.map(e => e.toUpperCase());
94+
target.functions = target.functions.map(e => ({
95+
...e,
96+
name: e.name.toUpperCase()
97+
}));
8598

8699
targets.addNewTarget(target);
87100
}

cli/src/targets/languages/rpgle.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@ export async function rpgleTargetCallback(targets: Targets, localPath: string, c
6565
// define exported functions
6666
if (cache.keyword[`NOMAIN`]) {
6767
ileObject.type = `MODULE`;
68-
69-
// Note that we store exports as uppercase.
70-
ileObject.exports = cache.procedures
71-
.filter((proc: any) => proc.keyword[`EXPORT`])
72-
.map(ref => ref.name.toUpperCase());
7368
}
7469

70+
ileObject.functions = cache.procedures.map(p => ({
71+
name: p.name.toUpperCase(),
72+
export: p.keyword[`EXPORT`] ? true : false,
73+
lineRange: [p.range.start, p.range.end]
74+
}));
75+
7576
infoOut(`${ileObject.systemName}.${ileObject.type}: ${ileObject.relativePath}`);
7677

7778
if (cache.includes && cache.includes.length > 0) {

cli/src/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,26 @@ export function getReferenceObjectsFrom(content: string) {
142142

143143
const newLine = content.includes(`\r\n`) ? `\r\n` : `\n`;
144144

145+
let lineNumber = -1;
145146
const lines = content.split(newLine);
146147

147148
let currentObject: ILEObject;
148149

149150
for (let line of lines) {
151+
lineNumber++;
152+
150153
const upperLine = line.trim().toUpperCase();
151154
if (upperLine.length === 0) continue;
152155
if (upperLine.startsWith(`#`)) continue;
153156

154157
// If the line starts with space, then it's an export of the parent
155158
if (line.startsWith(` `) || line.startsWith(`\t`)) {
156159
if (currentObject) {
157-
currentObject.exports.push(upperLine);
160+
currentObject.functions.push({
161+
name: upperLine,
162+
export: true,
163+
lineRange: [lineNumber, lineNumber]
164+
});
158165
}
159166

160167
} else {
@@ -167,7 +174,7 @@ export function getReferenceObjectsFrom(content: string) {
167174
currentObject = {
168175
systemName: objectParts[0],
169176
type: objectParts[1] as ObjectType, //TODO: validate type
170-
exports: [],
177+
functions: [],
171178
reference: true,
172179
}
173180

cli/test/cl.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ describe(`CL parsing / generating`, () => {
1010
expect(command).toBe('CRTSQLRPGI');
1111
expect(Object.keys(parameters)).toHaveLength(8);
1212

13-
console.log(parameters);
1413
expect(parameters[`compileopt`]).toBe(`'TGTCCSID(*JOB)'`);
1514

1615
const generated = toCl(command, parameters);

cli/test/cldclf.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ describe(`CL with DCLF`, () => {
3535
expect(apgm).toBeDefined();
3636

3737
const logs = targets.logger.getLogsFor(apgm.relativePath);
38-
console.log(logs);
3938
expect(logs.length).toBe(0);
4039

4140
expect(apgm.deps.length).toBe(1);

cli/test/cs_with_bnddir.test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,11 @@ describe(`pseudo tests`, () => {
105105

106106
const nept = resolvedObjects.find(f => f.systemName === `NEMP` && f.type === `FILE`);
107107
const targetsOut = makefile.generateTargets([nept]).join(`\n`);
108-
console.log(targetsOut);
109108

110109
expect(targetsOut).toContain(`all: .logs .evfevent library $(PREPATH)/NEMP.FILE`);
111110
expect(targetsOut).not.toContain(`$(PREPATH)/NEWEMP.PGM:`);
112111

113112
const rules = makefile.generateGenericRules([nept]).join(`\n`);
114-
console.log(rules);
115113

116114
expect(rules).toContain(`$(PREPATH)/NEMP.FILE:`);
117115
});
@@ -125,13 +123,11 @@ describe(`pseudo tests`, () => {
125123

126124
const nept = resolvedObjects.find(f => f.systemName === `NEWEMP` && f.type === `PGM`);
127125
const targetsOut = makefile.generateTargets([nept]).join(`\n`);
128-
console.log(targetsOut);
129126

130127
expect(targetsOut).toContain(`all: .logs .evfevent library $(PREPATH)/NEWEMP.PGM`);
131128
expect(targetsOut).toContain(`$(PREPATH)/NEWEMP.PGM:`);
132129

133130
const rules = makefile.generateGenericRules([nept]).join(`\n`);
134-
console.log(rules);
135131

136132
expect(rules).toContain(`$(PREPATH)/NEMP.FILE:`);
137133
});
@@ -145,13 +141,11 @@ describe(`pseudo tests`, () => {
145141

146142
const nept = resolvedObjects.find(f => f.systemName === `NEWEMP` && f.type === `PGM`);
147143
const targetsOut = makefile.generateTargets([nept]).join(`\n`);
148-
console.log(targetsOut);
149144

150145
expect(targetsOut).toContain(`all: .logs .evfevent library $(PREPATH)/NEWEMP.PGM`);
151146
expect(targetsOut).not.toContain(`$(PREPATH)/NEWEMP.PGM:`);
152147

153148
const rules = makefile.generateGenericRules([nept]).join(`\n`);
154-
console.log(rules);
155149

156150
expect(rules).not.toContain(`$(PREPATH)/NEMP.FILE:`);
157151
expect(rules).toContain(`$(PREPATH)/NEWEMP.PGM: qrpglesrc/newemp.pgm.sqlrpgle`);
@@ -166,13 +160,11 @@ describe(`pseudo tests`, () => {
166160

167161
const nept = resolvedObjects.find(f => f.systemName === `NEMP` && f.type === `FILE`);
168162
const targetsOut = makefile.generateTargets([nept]).join(`\n`);
169-
console.log(targetsOut);
170163

171164
expect(targetsOut).toContain(`all: .logs .evfevent library $(PREPATH)/NEMP.FILE $(PREPATH)/NEWEMP.PGM $(PREPATH)/DEPTS.PGM`);
172165
expect(targetsOut).not.toContain(`$(PREPATH)/NEWEMP.PGM:`);
173166

174167
const rules = makefile.generateGenericRules([nept]).join(`\n`);
175-
console.log(rules);
176168

177169
expect(rules).toContain(`$(PREPATH)/NEMP.FILE:`);
178170
expect(rules).toContain(`$(PREPATH)/NEWEMP.PGM:`);
@@ -189,13 +181,11 @@ describe(`pseudo tests`, () => {
189181

190182
const nept = resolvedObjects.find(f => f.systemName === `NEMP` && f.type === `FILE`);
191183
const targetsOut = makefile.generateTargets([nept]).join(`\n`);
192-
console.log(targetsOut);
193184

194185
expect(targetsOut).toContain(`all: .logs .evfevent library $(PREPATH)/NEMP.FILE $(PREPATH)/NEWEMP.PGM $(PREPATH)/DEPTS.PGM`);
195186
expect(targetsOut).toContain(`$(PREPATH)/NEWEMP.PGM: $(PREPATH)/EMPLOYEE.FILE $(PREPATH)/NEMP.FILE`);
196187

197188
const rules = makefile.generateGenericRules([nept]).join(`\n`);
198-
console.log(rules);
199189

200190
expect(rules).toContain(`$(PREPATH)/NEMP.FILE:`);
201191
expect(rules).toContain(`$(PREPATH)/NEWEMP.PGM:`);

cli/test/environment.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,15 @@ describe(`Reference files`, () => {
153153
expect(refObjects[1]).toMatchObject({
154154
type: `SRVPGM`,
155155
systemName: `UTILS`,
156-
exports: [`TOUPPER`, `TOLOWER`]
156+
functions: [{
157+
name: `TOUPPER`,
158+
export: true,
159+
lineRange: [6, 6]
160+
}, {
161+
name: `TOLOWER`,
162+
export: true,
163+
lineRange: [7, 7]
164+
}],
157165
});
158166
});
159167
})

cli/test/fixtures/targets.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,19 @@ export async function baseTargets(withDeps = false) {
5252
expect(moduleA.type).toBe(`MODULE`);
5353
expect(moduleA.extension).toBe(`rpgle`);
5454
expect(moduleA.relativePath).toBe(path.join(`qrpglesrc`, `moduleA.rpgle`));
55-
moduleA.exports = [`SUMNUMS`];
55+
moduleA.functions = [
56+
{name: `SUMNUMS`, export: true, lineRange: [0, 0]},
57+
]
5658

5759
// Module MODULEB.MODULE, which exports TOLOWER
5860
const moduleB = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `moduleB.sqlrpgle`));
5961
expect(moduleB.systemName).toBe(`MODULEB`);
6062
expect(moduleB.type).toBe(`MODULE`);
6163
expect(moduleB.extension).toBe(`sqlrpgle`);
6264
expect(moduleB.relativePath).toBe(path.join(`qrpglesrc`, `moduleB.sqlrpgle`));
63-
moduleB.exports = [`TOLOWER`];
65+
moduleB.functions = [
66+
{name: `TOLOWER`, export: true, lineRange: [0, 0]},
67+
];
6468

6569
// SRVPGMA.SRVPGM, which imports TOLOWER from MODULEB.MODULE and therefore exports TOLOWER
6670
const srvpgmAModule = await targets.resolvePathToObject(path.join(cwd, `qsrvsrc`, `srvpgmA.bnd`));
@@ -69,7 +73,9 @@ export async function baseTargets(withDeps = false) {
6973
expect(srvpgmAModule.extension).toBe(`bnd`);
7074
expect(srvpgmAModule.relativePath).toBe(path.join(`qsrvsrc`, `srvpgmA.bnd`));
7175
srvpgmAModule.imports = [`TOLOWER`];
72-
srvpgmAModule.exports = [`TOLOWER`];
76+
srvpgmAModule.functions = [
77+
{name: `TOLOWER`, export: true, lineRange: [0, 0]},
78+
];
7379

7480
// FILEA.FILE
7581
const fileA = await targets.resolvePathToObject(path.join(cwd, `qddssrc`, `fileA.sql`));
@@ -87,7 +93,9 @@ export async function baseTargets(withDeps = false) {
8793

8894
// ORDENTSRV.SRVPGM, which exports/imports FIXTOTALS
8995
const ORDENTSRV = await targets.resolvePathToObject(path.join(cwd, `qbndsrc`, `ordentsrv.binder`));
90-
ORDENTSRV.exports = [`FIXTOTALS`];
96+
ORDENTSRV.functions = [
97+
{name: `FIXTOTALS`, export: true, lineRange: [0, 0]}
98+
]
9199
ORDENTSRV.imports = [`FIXTOTALS`];
92100
expect(ORDENTSRV.systemName).toBe(`ORDENTSRV`);
93101
expect(ORDENTSRV.type).toBe(`SRVPGM`);
@@ -96,15 +104,19 @@ export async function baseTargets(withDeps = false) {
96104

97105
// ORDENTMOD.MODULE which exports FIXTOTALS
98106
const ORDENTMOD = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `ordentmod.rpgle`));
99-
ORDENTMOD.exports = [`FIXTOTALS`];
107+
ORDENTMOD.functions = [
108+
{name: `FIXTOTALS`, export: true, lineRange: [0, 0]}
109+
];
100110
expect(ORDENTMOD.systemName).toBe(`ORDENTMOD`);
101111
expect(ORDENTMOD.type).toBe(`MODULE`);
102112
expect(ORDENTMOD.extension).toBe(`rpgle`);
103113
expect(ORDENTMOD.relativePath).toBe(path.join(`qrpglesrc`, `ordentmod.rpgle`));
104114

105115
// UNUSEDSRV.SRVPGM, which exports BIGNOPE and is not used.
106116
const UNUSEDSRV = await targets.resolvePathToObject(path.join(cwd, `qbndsrc`, `unusedsrv.binder`));
107-
UNUSEDSRV.exports = [`BIGNOPE`];
117+
UNUSEDSRV.functions = [
118+
{name: `BIGNOPE`, export: true, lineRange: [0, 0]}
119+
];
108120
expect(UNUSEDSRV.systemName).toBe(`UNUSEDSRV`);
109121
expect(UNUSEDSRV.type).toBe(`SRVPGM`);
110122
expect(UNUSEDSRV.extension).toBe(`binder`);
@@ -138,26 +150,37 @@ export async function multiModuleObjects() {
138150

139151
// Module that is required by the MYWEBAPP.PGM
140152
const handlerAMod = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `handlerA.rpgle`));
141-
handlerAMod.exports = [`ROUTEHANDLERA`];
153+
handlerAMod.functions = [
154+
{name: `ROUTEHANDLERA`, export: true, lineRange: [0, 0]}
155+
]
142156
handlerAMod.imports = [`JSON_SQLRESULTSET`, `IL_RESPONSEWRITESTREAM`];
143157

144158
// Another module that is required by the MYWEBAPP.PGM
145159
const handlerBMod = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `handlerB.rpgle`));
146-
handlerBMod.exports = [`ROUTEHANDLERB`];
160+
handlerBMod.functions = [
161+
{name: `ROUTEHANDLERB`, export: true, lineRange: [0, 0]}
162+
]
147163
handlerBMod.imports = [`API_VALIDATE`, `JSON_SQLRESULTSET`, `IL_RESPONSEWRITESTREAM`];
148164

149165
// Another module that is part of the JWTHANDLER.SRVPGM object
150166
const jwtHandlerMod = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `jwtHandler.rpgle`));
151-
jwtHandlerMod.exports = [`JWT_MIDDLEWARE`];
167+
jwtHandlerMod.functions = [
168+
{name: `JWT_MIDDLEWARE`, export: true, lineRange: [0, 0]}
169+
]
152170

153171
// Another module that is part of the JWTHANDLER.SRVPGM object
154172
const validateMod = await targets.resolvePathToObject(path.join(cwd, `qrpglesrc`, `validate.rpgle`));
155-
validateMod.exports = [`API_VALIDATE`];
173+
validateMod.functions = [
174+
{name: `API_VALIDATE`, export: true, lineRange: [0, 0]}
175+
];
156176

157177
// Service program for JWTHANDLER, used by MYWEBAPP
158178
const jwtHandlerSrv = await targets.resolvePathToObject(path.join(cwd, `qsrvsrc`, `utils.binder`));
159179
jwtHandlerSrv.imports = [`JWT_MIDDLEWARE`, `API_VALIDATE`];
160-
jwtHandlerSrv.exports = [`JWT_MIDDLEWARE`, `API_VALIDATE`];
180+
jwtHandlerSrv.functions = [
181+
{name: `JWT_MIDDLEWARE`, export: true, lineRange: [0, 0]},
182+
{name: `API_VALIDATE`, export: true, lineRange: [0, 0]}
183+
];
161184

162185
targets.createOrAppend(myWebApp);
163186
targets.createOrAppend(handlerAMod);

0 commit comments

Comments
 (0)