@jhlagado/azm 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/api-compile.d.ts +7 -1
- package/dist/src/api-compile.js +17 -5
- package/dist/src/api-register-contracts.js +69 -2
- package/dist/src/api-tooling.d.ts +1 -1
- package/dist/src/assembly/import-visibility.js +108 -33
- package/dist/src/cli/artifact-files.d.ts +1 -0
- package/dist/src/cli/artifact-files.js +5 -0
- package/dist/src/cli/parse-args.d.ts +6 -1
- package/dist/src/cli/parse-args.js +59 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/usage.js +5 -0
- package/dist/src/cli/write-artifacts.d.ts +1 -1
- package/dist/src/cli/write-artifacts.js +15 -2
- package/dist/src/core/compile.js +1 -0
- package/dist/src/expansion/op-expand-selected.js +8 -1
- package/dist/src/expansion/op-expansion.d.ts +3 -0
- package/dist/src/expansion/op-expansion.js +12 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/node/source-host.js +5 -2
- package/dist/src/outputs/types.d.ts +13 -1
- package/dist/src/register-contracts/analyze-helpers.d.ts +6 -1
- package/dist/src/register-contracts/analyze-helpers.js +67 -0
- package/dist/src/register-contracts/analyze.d.ts +8 -1
- package/dist/src/register-contracts/analyze.js +353 -16
- package/dist/src/register-contracts/interfaceContracts.js +45 -0
- package/dist/src/register-contracts/liveness.js +23 -0
- package/dist/src/register-contracts/policy.d.ts +2 -0
- package/dist/src/register-contracts/policy.js +54 -0
- package/dist/src/register-contracts/programModel-boundaries.d.ts +5 -1
- package/dist/src/register-contracts/programModel-boundaries.js +20 -5
- package/dist/src/register-contracts/programModel-routines.js +37 -6
- package/dist/src/register-contracts/ratchet.d.ts +3 -0
- package/dist/src/register-contracts/ratchet.js +88 -0
- package/dist/src/register-contracts/report.d.ts +8 -1
- package/dist/src/register-contracts/report.js +174 -0
- package/dist/src/register-contracts/smartCommentParsing.js +22 -0
- package/dist/src/register-contracts/summary-boundary.js +9 -2
- package/dist/src/register-contracts/tooling.d.ts +2 -1
- package/dist/src/register-contracts/tooling.js +2 -0
- package/dist/src/register-contracts/types.d.ts +157 -0
- package/dist/src/source/logical-lines.d.ts +1 -0
- package/dist/src/source/source-span.d.ts +1 -0
- package/dist/src/syntax/parse-layout-declarations.js +1 -0
- package/dist/src/syntax/parse-line.js +4 -0
- package/docs/codebase/02-source-loading-and-parsing.md +10 -6
- package/docs/codebase/04-ops-and-register-contracts.md +49 -4
- package/docs/codebase/05-interfaces-and-output-artifacts.md +56 -6
- package/docs/codebase/06-verification-and-maintenance.md +10 -2
- package/docs/codebase/appendices/a-directory-file-reference.md +3 -1
- package/docs/codebase/appendices/b-compile-flow-reference.md +7 -5
- package/docs/codebase/appendices/c-public-surface-reference.md +19 -5
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { writeHex } from './outputs/write-hex.js';
|
|
|
3
3
|
import type { AddressRange, Artifact, D8mArtifact, D8mFileEntry, D8mFileSymbol, D8mGenerator, D8mJson, D8mSegment, D8mSymbol, EmittedByteMap, FormatWriters, SymbolEntry, WriteD8mOptions } from './outputs/types.js';
|
|
4
4
|
import type { Diagnostic } from './model/diagnostic.js';
|
|
5
5
|
import type { CaseStyleMode } from './tooling/case-style.js';
|
|
6
|
-
import type { RegisterContractsMode } from './register-contracts/types.js';
|
|
6
|
+
import type { RegisterContractsMode, RegisterContractsPolicy, RegisterContractsReportFormat } from './register-contracts/types.js';
|
|
7
7
|
export { writeHex, defaultFormatWriters };
|
|
8
8
|
export type { AddressRange, Artifact, D8mArtifact, D8mFileEntry, D8mFileSymbol, D8mGenerator, D8mJson, D8mSegment, D8mSymbol, EmittedByteMap, FormatWriters, SymbolEntry, WriteD8mOptions, };
|
|
9
9
|
export type CompileDependencies = CompileNextDependencies;
|
|
@@ -30,8 +30,14 @@ export interface CompileNextFunctionOptions {
|
|
|
30
30
|
readonly registerContracts?: RegisterContractsMode;
|
|
31
31
|
/** @deprecated Use registerContracts. */
|
|
32
32
|
readonly registerCare?: RegisterContractsMode;
|
|
33
|
+
readonly registerContractsPolicy?: RegisterContractsPolicy;
|
|
33
34
|
readonly emitRegisterReport?: boolean;
|
|
35
|
+
readonly registerContractsReportFormat?: RegisterContractsReportFormat;
|
|
36
|
+
readonly registerContractsBaseline?: string;
|
|
37
|
+
readonly registerContractsRatchet?: boolean;
|
|
34
38
|
readonly emitRegisterInterface?: boolean;
|
|
39
|
+
readonly emitRegisterInference?: boolean;
|
|
40
|
+
readonly registerContractsInferenceFormat?: 'json' | 'markdown';
|
|
35
41
|
readonly emitRegisterAnnotations?: boolean;
|
|
36
42
|
readonly fixRegisterContracts?: boolean;
|
|
37
43
|
readonly acceptRegisterOutputCandidates?: string[];
|
package/dist/src/api-compile.js
CHANGED
|
@@ -5,12 +5,13 @@ import { runRegisterContracts, shouldAnalyzeRegisterContracts } from './api-regi
|
|
|
5
5
|
import { analyzeProgramNext, loadProgramNext } from './tooling/api.js';
|
|
6
6
|
import { defaultFormatWriters } from './outputs/index.js';
|
|
7
7
|
import { writeHex } from './outputs/write-hex.js';
|
|
8
|
+
import { registerContractsPolicyModeForFile } from './register-contracts/policy.js';
|
|
8
9
|
import { buildRegisterContractsProgramModel } from './register-contracts/programModel.js';
|
|
9
10
|
function parseUnresolvedSymbolName(message) {
|
|
10
11
|
const match = /^Unresolved symbol "([^"]+)"/.exec(message);
|
|
11
12
|
return match?.[1];
|
|
12
13
|
}
|
|
13
|
-
function isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls) {
|
|
14
|
+
function isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls, policy, fallbackMode) {
|
|
14
15
|
if (directCalls === undefined || directCalls.length === 0) {
|
|
15
16
|
return false;
|
|
16
17
|
}
|
|
@@ -27,7 +28,11 @@ function isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCall
|
|
|
27
28
|
return directCalls.some((call) => call.target === symbol &&
|
|
28
29
|
call.file === diagnostic.sourceName &&
|
|
29
30
|
call.line === diagnostic.line &&
|
|
30
|
-
call.column === diagnostic.column
|
|
31
|
+
call.column === diagnostic.column &&
|
|
32
|
+
!isRegisterContractsPolicyOffForFile(call.file, policy, fallbackMode));
|
|
33
|
+
}
|
|
34
|
+
function isRegisterContractsPolicyOffForFile(file, policy, fallbackMode) {
|
|
35
|
+
return policy !== undefined && registerContractsPolicyModeForFile(file, policy, fallbackMode) === 'off';
|
|
31
36
|
}
|
|
32
37
|
export { writeHex, defaultFormatWriters };
|
|
33
38
|
/**
|
|
@@ -56,10 +61,14 @@ export async function compile(entryFile, options = {}, deps = { formats: default
|
|
|
56
61
|
.directCalls
|
|
57
62
|
: undefined;
|
|
58
63
|
diagnostics.push(...analysis.diagnostics.filter((diagnostic) => analyzeRegisterContractsNow
|
|
59
|
-
? !isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls)
|
|
64
|
+
? !isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls, options.registerContractsPolicy, options.registerContracts ?? options.registerCare)
|
|
60
65
|
: true));
|
|
61
66
|
const artifacts = [];
|
|
62
67
|
if (hasErrors(diagnostics)) {
|
|
68
|
+
if (analyzeRegisterContractsNow && options.emitRegisterReport === true) {
|
|
69
|
+
const registerContracts = await runRegisterContracts(loaded.loadedProgram, options);
|
|
70
|
+
artifacts.push(...registerContractsReportArtifacts(registerContracts.artifacts));
|
|
71
|
+
}
|
|
63
72
|
sortDiagnosticsInPlace(diagnostics);
|
|
64
73
|
return { diagnostics, artifacts };
|
|
65
74
|
}
|
|
@@ -76,11 +85,11 @@ export async function compile(entryFile, options = {}, deps = { formats: default
|
|
|
76
85
|
const program = loaded.loadedProgram.program.files[0]?.items ?? [];
|
|
77
86
|
const assembled = assembleProgram(program);
|
|
78
87
|
diagnostics.push(...assembled.diagnostics.filter((diagnostic) => analyzeRegisterContractsNow
|
|
79
|
-
? !isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls)
|
|
88
|
+
? !isSuppressedUnknownSymbolInRegisterContractsMode(diagnostic, directCalls, options.registerContractsPolicy, options.registerContracts ?? options.registerCare)
|
|
80
89
|
: true));
|
|
81
90
|
sortDiagnosticsInPlace(diagnostics);
|
|
82
91
|
if (hasErrors(diagnostics)) {
|
|
83
|
-
return { diagnostics, artifacts:
|
|
92
|
+
return { diagnostics, artifacts: registerContractsReportArtifacts(artifacts) };
|
|
84
93
|
}
|
|
85
94
|
const emittedArtifacts = await emitAssemblyArtifacts({
|
|
86
95
|
entryFile: normalizedEntry,
|
|
@@ -100,6 +109,9 @@ export async function compile(entryFile, options = {}, deps = { formats: default
|
|
|
100
109
|
function hasErrors(diagnostics) {
|
|
101
110
|
return diagnostics.some((diagnostic) => diagnostic.severity === 'error');
|
|
102
111
|
}
|
|
112
|
+
function registerContractsReportArtifacts(artifacts) {
|
|
113
|
+
return artifacts.filter((artifact) => artifact.kind === 'register-contracts-report');
|
|
114
|
+
}
|
|
103
115
|
function sortDiagnosticsInPlace(diagnostics) {
|
|
104
116
|
dedupeDiagnosticsInPlace(diagnostics);
|
|
105
117
|
diagnostics.sort((left, right) => {
|
|
@@ -8,10 +8,13 @@ export function shouldAnalyzeRegisterContracts(options) {
|
|
|
8
8
|
return (registerContractsMode !== 'off' ||
|
|
9
9
|
options.emitRegisterReport === true ||
|
|
10
10
|
options.emitRegisterInterface === true ||
|
|
11
|
+
options.emitRegisterInference === true ||
|
|
11
12
|
options.emitRegisterAnnotations === true ||
|
|
12
13
|
options.fixRegisterContracts === true ||
|
|
14
|
+
options.registerContractsPolicy !== undefined ||
|
|
13
15
|
(options.acceptRegisterOutputCandidates?.length ?? 0) > 0 ||
|
|
14
|
-
(options.registerContractsInterfaces?.length ?? options.registerCareInterfaces?.length ?? 0) > 0
|
|
16
|
+
(options.registerContractsInterfaces?.length ?? options.registerCareInterfaces?.length ?? 0) > 0 ||
|
|
17
|
+
options.registerContractsBaseline !== undefined);
|
|
15
18
|
}
|
|
16
19
|
export async function runRegisterContracts(loadedProgram, options) {
|
|
17
20
|
const diagnostics = [];
|
|
@@ -20,10 +23,24 @@ export async function runRegisterContracts(loadedProgram, options) {
|
|
|
20
23
|
if (hasErrors(diagnostics)) {
|
|
21
24
|
return { diagnostics, artifacts };
|
|
22
25
|
}
|
|
26
|
+
const baselineReport = await loadBaselineReport(options.registerContractsBaseline, diagnostics);
|
|
27
|
+
if (hasErrors(diagnostics)) {
|
|
28
|
+
return { diagnostics, artifacts };
|
|
29
|
+
}
|
|
23
30
|
const registerContracts = analyzeRegisterContracts(loadedProgram, {
|
|
24
31
|
mode: options.registerContracts ?? options.registerCare ?? 'off',
|
|
32
|
+
...(options.registerContractsPolicy !== undefined
|
|
33
|
+
? { policy: options.registerContractsPolicy }
|
|
34
|
+
: {}),
|
|
25
35
|
emitReport: options.emitRegisterReport === true,
|
|
36
|
+
...(options.registerContractsReportFormat !== undefined
|
|
37
|
+
? { reportFormat: options.registerContractsReportFormat }
|
|
38
|
+
: {}),
|
|
26
39
|
emitInterface: options.emitRegisterInterface === true,
|
|
40
|
+
emitInference: options.emitRegisterInference === true,
|
|
41
|
+
...(options.registerContractsInferenceFormat !== undefined
|
|
42
|
+
? { inferenceFormat: options.registerContractsInferenceFormat }
|
|
43
|
+
: {}),
|
|
27
44
|
emitAnnotations: options.emitRegisterAnnotations === true || options.fixRegisterContracts === true,
|
|
28
45
|
fixRegisterContracts: options.fixRegisterContracts === true,
|
|
29
46
|
acceptedOutputCandidates: parseAcceptedOutputCandidates(options.acceptRegisterOutputCandidates ?? []),
|
|
@@ -33,13 +50,36 @@ export async function runRegisterContracts(loadedProgram, options) {
|
|
|
33
50
|
}
|
|
34
51
|
: {}),
|
|
35
52
|
...(interfaceContracts.length > 0 ? { interfaceContracts } : {}),
|
|
53
|
+
...(baselineReport !== undefined ? { baselineReport } : {}),
|
|
54
|
+
...(options.registerContractsBaseline !== undefined
|
|
55
|
+
? { baselineFile: normalize(options.registerContractsBaseline) }
|
|
56
|
+
: {}),
|
|
57
|
+
ratchet: options.registerContractsRatchet === true,
|
|
36
58
|
});
|
|
37
59
|
if (registerContracts.reportText !== undefined) {
|
|
38
|
-
artifacts.push({
|
|
60
|
+
artifacts.push({
|
|
61
|
+
kind: 'register-contracts-report',
|
|
62
|
+
...(registerContracts.reportFormat !== undefined
|
|
63
|
+
? { format: registerContracts.reportFormat }
|
|
64
|
+
: {}),
|
|
65
|
+
text: registerContracts.reportText,
|
|
66
|
+
...(registerContracts.reportJson !== undefined ? { json: registerContracts.reportJson } : {}),
|
|
67
|
+
...(registerContracts.findings !== undefined ? { findings: registerContracts.findings } : {}),
|
|
68
|
+
});
|
|
39
69
|
}
|
|
40
70
|
if (registerContracts.interfaceText !== undefined) {
|
|
41
71
|
artifacts.push({ kind: 'register-contracts-interface', text: registerContracts.interfaceText });
|
|
42
72
|
}
|
|
73
|
+
if (registerContracts.inferenceText !== undefined) {
|
|
74
|
+
artifacts.push({
|
|
75
|
+
kind: 'register-contracts-inference',
|
|
76
|
+
format: registerContracts.inferenceFormat ?? 'json',
|
|
77
|
+
text: registerContracts.inferenceText,
|
|
78
|
+
...(registerContracts.inferenceJson !== undefined
|
|
79
|
+
? { json: registerContracts.inferenceJson }
|
|
80
|
+
: {}),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
43
83
|
if (registerContracts.annotations !== undefined && registerContracts.annotations.length > 0) {
|
|
44
84
|
artifacts.push({
|
|
45
85
|
kind: 'register-contracts-annotations',
|
|
@@ -52,6 +92,33 @@ export async function runRegisterContracts(loadedProgram, options) {
|
|
|
52
92
|
diagnostics.push(...registerContracts.diagnostics);
|
|
53
93
|
return { artifacts, diagnostics };
|
|
54
94
|
}
|
|
95
|
+
async function loadBaselineReport(rawPath, diagnostics) {
|
|
96
|
+
if (rawPath === undefined)
|
|
97
|
+
return undefined;
|
|
98
|
+
const baselinePath = normalize(rawPath);
|
|
99
|
+
try {
|
|
100
|
+
const parsed = JSON.parse(await readFile(baselinePath, 'utf8'));
|
|
101
|
+
if (parsed.format !== 'azm-register-contracts-report' || !Array.isArray(parsed.findings)) {
|
|
102
|
+
diagnostics.push({
|
|
103
|
+
severity: 'error',
|
|
104
|
+
code: 'AZMN_REGISTER_CONTRACTS',
|
|
105
|
+
sourceName: baselinePath,
|
|
106
|
+
message: 'Register contracts baseline must be a JSON register-contracts report',
|
|
107
|
+
});
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
diagnostics.push({
|
|
114
|
+
severity: 'error',
|
|
115
|
+
code: 'AZMN_REGISTER_CONTRACTS',
|
|
116
|
+
sourceName: baselinePath,
|
|
117
|
+
message: `Unable to read register contracts baseline: ${String(error)}`,
|
|
118
|
+
});
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
55
122
|
async function loadInterfaceContracts(interfaces, diagnostics) {
|
|
56
123
|
const interfaceContracts = [];
|
|
57
124
|
for (const rawInterface of interfaces) {
|
|
@@ -4,4 +4,4 @@ export { DiagnosticIds } from './model/diagnostic.js';
|
|
|
4
4
|
export type { AnalyzeProgramOptions, AnalyzeProgramResult, LoadedProgram, LoadProgramOptions, LoadProgramResult, AnalyzeProgramNextOptions, AnalyzeProgramNextResult, LoadedProgramNext, LoadProgramNextOptions, LoadProgramNextResult, } from './tooling/api.js';
|
|
5
5
|
export type { CaseStyleMode } from './tooling/case-style.js';
|
|
6
6
|
export type { Diagnostic, DiagnosticId, DiagnosticSeverity } from './model/diagnostic.js';
|
|
7
|
-
export type { RegisterCareMode, RegisterCareOutputCandidate, RegisterCareUnit, RegisterContractsMode, RegisterContractsOutputCandidate, RegisterContractsUnit, } from './register-contracts/types.js';
|
|
7
|
+
export type { RegisterCareMode, RegisterCareOutputCandidate, RegisterCareUnit, RegisterContractsMode, RegisterContractsFinding, RegisterContractsFindingKind, RegisterContractsOutputCandidate, RegisterContractsUnit, } from './register-contracts/types.js';
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import { diagnostic } from '../semantics/diagnostics.js';
|
|
2
2
|
export function validateImportVisibility(items, diagnostics) {
|
|
3
|
-
const
|
|
3
|
+
const symbols = collectSymbolVisibility(items);
|
|
4
4
|
for (const item of items) {
|
|
5
|
-
validateItemReferences(item,
|
|
5
|
+
validateItemReferences(item, symbols, diagnostics);
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
function
|
|
8
|
+
function collectSymbolVisibility(items) {
|
|
9
9
|
const labels = new Map();
|
|
10
|
+
const exactSymbols = new Set();
|
|
10
11
|
const importedSourceUnits = importedUnitNames(items);
|
|
12
|
+
const symbolConflicts = buildSymbolConflictIndex(items);
|
|
11
13
|
for (const item of items) {
|
|
14
|
+
for (const name of exactSymbolNames(item)) {
|
|
15
|
+
exactSymbols.add(name);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
19
|
+
const item = items[index];
|
|
12
20
|
if (item.kind !== 'label')
|
|
13
21
|
continue;
|
|
14
22
|
labels.set(item.name, {
|
|
@@ -16,14 +24,75 @@ function collectLabelVisibility(items) {
|
|
|
16
24
|
definingSourceUnit: item.span.sourceUnit,
|
|
17
25
|
definingSourceName: item.span.sourceName,
|
|
18
26
|
public: isPublicLabel(item, importedSourceUnits),
|
|
27
|
+
duplicateName: hasAddressPlanningNameConflict(item.name, symbolConflicts, items, index),
|
|
19
28
|
});
|
|
20
29
|
}
|
|
21
|
-
return labels;
|
|
30
|
+
return { labels, exactSymbols };
|
|
31
|
+
}
|
|
32
|
+
function buildSymbolConflictIndex(items) {
|
|
33
|
+
const exact = new Map();
|
|
34
|
+
const declarationLower = new Map();
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
for (const name of exactSymbolNames(item)) {
|
|
37
|
+
exact.set(name, (exact.get(name) ?? 0) + 1);
|
|
38
|
+
}
|
|
39
|
+
const declarationName = caseInsensitiveDeclarationName(item);
|
|
40
|
+
if (declarationName !== undefined) {
|
|
41
|
+
const key = declarationName.toLowerCase();
|
|
42
|
+
declarationLower.set(key, (declarationLower.get(key) ?? 0) + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { exact, declarationLower };
|
|
46
|
+
}
|
|
47
|
+
function hasAddressPlanningNameConflict(labelName, conflicts, items, labelIndex) {
|
|
48
|
+
return ((conflicts.exact.get(labelName) ?? 0) > 1 ||
|
|
49
|
+
(conflicts.declarationLower.get(labelName.toLowerCase()) ?? 0) > 0 ||
|
|
50
|
+
hasReportedEnumMemberConflict(labelName, items, labelIndex));
|
|
51
|
+
}
|
|
52
|
+
function hasReportedEnumMemberConflict(labelName, items, labelIndex) {
|
|
53
|
+
const lowerName = labelName.toLowerCase();
|
|
54
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
55
|
+
const item = items[index];
|
|
56
|
+
if (item.kind !== 'enum')
|
|
57
|
+
continue;
|
|
58
|
+
for (const memberName of qualifiedEnumMemberNames(item)) {
|
|
59
|
+
if (memberName === labelName)
|
|
60
|
+
return true;
|
|
61
|
+
if (index > labelIndex && memberName.toLowerCase() === lowerName)
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function exactSymbolNames(item) {
|
|
68
|
+
switch (item.kind) {
|
|
69
|
+
case 'label':
|
|
70
|
+
case 'equ':
|
|
71
|
+
return [item.name];
|
|
72
|
+
case 'enum':
|
|
73
|
+
return qualifiedEnumMemberNames(item);
|
|
74
|
+
default:
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function qualifiedEnumMemberNames(item) {
|
|
79
|
+
return item.kind === 'enum' ? item.members.map((member) => `${item.name}.${member}`) : [];
|
|
80
|
+
}
|
|
81
|
+
function caseInsensitiveDeclarationName(item) {
|
|
82
|
+
switch (item.kind) {
|
|
83
|
+
case 'enum':
|
|
84
|
+
case 'type':
|
|
85
|
+
case 'type-alias':
|
|
86
|
+
return item.name;
|
|
87
|
+
default:
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
22
90
|
}
|
|
23
91
|
function importedUnitNames(items) {
|
|
24
92
|
const units = new Set();
|
|
25
93
|
for (const item of items) {
|
|
26
|
-
if (item.span.
|
|
94
|
+
if (item.span.sourceUnitRelation === 'import' &&
|
|
95
|
+
item.span.sourceUnit !== undefined) {
|
|
27
96
|
units.add(item.span.sourceUnit);
|
|
28
97
|
}
|
|
29
98
|
}
|
|
@@ -34,39 +103,39 @@ function isPublicLabel(item, importedSourceUnits) {
|
|
|
34
103
|
item.span.sourceUnit === undefined ||
|
|
35
104
|
!importedSourceUnits.has(item.span.sourceUnit));
|
|
36
105
|
}
|
|
37
|
-
function validateItemReferences(item,
|
|
106
|
+
function validateItemReferences(item, symbols, diagnostics) {
|
|
38
107
|
switch (item.kind) {
|
|
39
108
|
case 'org':
|
|
40
|
-
validateExpression(item.expression, item.span,
|
|
109
|
+
validateExpression(item.expression, item.span, symbols, diagnostics);
|
|
41
110
|
return;
|
|
42
111
|
case 'equ':
|
|
43
|
-
validateExpression(item.expression, item.span,
|
|
112
|
+
validateExpression(item.expression, item.span, symbols, diagnostics);
|
|
44
113
|
return;
|
|
45
114
|
case 'db':
|
|
46
115
|
for (const value of item.values) {
|
|
47
|
-
validateDataValue(value, item.span,
|
|
116
|
+
validateDataValue(value, item.span, symbols, diagnostics);
|
|
48
117
|
}
|
|
49
118
|
return;
|
|
50
119
|
case 'dw':
|
|
51
120
|
for (const value of item.values) {
|
|
52
|
-
validateExpression(value, item.span,
|
|
121
|
+
validateExpression(value, item.span, symbols, diagnostics);
|
|
53
122
|
}
|
|
54
123
|
return;
|
|
55
124
|
case 'ds':
|
|
56
|
-
validateExpression(item.size, item.span,
|
|
125
|
+
validateExpression(item.size, item.span, symbols, diagnostics);
|
|
57
126
|
if (item.fill !== undefined) {
|
|
58
|
-
validateExpression(item.fill, item.span,
|
|
127
|
+
validateExpression(item.fill, item.span, symbols, diagnostics);
|
|
59
128
|
}
|
|
60
129
|
return;
|
|
61
130
|
case 'align':
|
|
62
|
-
validateExpression(item.alignment, item.span,
|
|
131
|
+
validateExpression(item.alignment, item.span, symbols, diagnostics);
|
|
63
132
|
return;
|
|
64
133
|
case 'binfrom':
|
|
65
134
|
case 'binto':
|
|
66
|
-
validateExpression(item.expression, item.span,
|
|
135
|
+
validateExpression(item.expression, item.span, symbols, diagnostics);
|
|
67
136
|
return;
|
|
68
137
|
case 'instruction':
|
|
69
|
-
validateInstruction(item.instruction, item.span,
|
|
138
|
+
validateInstruction(item.instruction, item.span, symbols, diagnostics);
|
|
70
139
|
return;
|
|
71
140
|
case 'label':
|
|
72
141
|
case 'comment':
|
|
@@ -78,14 +147,14 @@ function validateItemReferences(item, labels, diagnostics) {
|
|
|
78
147
|
return;
|
|
79
148
|
}
|
|
80
149
|
}
|
|
81
|
-
function validateDataValue(value, span,
|
|
150
|
+
function validateDataValue(value, span, symbols, diagnostics) {
|
|
82
151
|
if ('kind' in value && value.kind === 'string-fragment')
|
|
83
152
|
return;
|
|
84
|
-
validateExpression(value, span,
|
|
153
|
+
validateExpression(value, span, symbols, diagnostics);
|
|
85
154
|
}
|
|
86
|
-
function validateInstruction(instruction, span,
|
|
155
|
+
function validateInstruction(instruction, span, symbols, diagnostics) {
|
|
87
156
|
for (const expression of instructionExpressions(instruction)) {
|
|
88
|
-
validateExpression(expression, span,
|
|
157
|
+
validateExpression(expression, span, symbols, diagnostics);
|
|
89
158
|
}
|
|
90
159
|
}
|
|
91
160
|
function instructionExpressions(instruction) {
|
|
@@ -154,49 +223,55 @@ function operandExpressions(operand) {
|
|
|
154
223
|
return [];
|
|
155
224
|
}
|
|
156
225
|
}
|
|
157
|
-
function validateExpression(expression, span,
|
|
226
|
+
function validateExpression(expression, span, symbols, diagnostics) {
|
|
158
227
|
switch (expression.kind) {
|
|
159
228
|
case 'symbol':
|
|
160
|
-
validateSymbolReference(expression.name, span,
|
|
229
|
+
validateSymbolReference(expression.name, span, symbols, diagnostics);
|
|
161
230
|
return;
|
|
162
231
|
case 'byte-function':
|
|
163
232
|
case 'unary':
|
|
164
|
-
validateExpression(expression.expression, span,
|
|
233
|
+
validateExpression(expression.expression, span, symbols, diagnostics);
|
|
165
234
|
return;
|
|
166
235
|
case 'binary':
|
|
167
|
-
validateExpression(expression.left, span,
|
|
168
|
-
validateExpression(expression.right, span,
|
|
236
|
+
validateExpression(expression.left, span, symbols, diagnostics);
|
|
237
|
+
validateExpression(expression.right, span, symbols, diagnostics);
|
|
169
238
|
return;
|
|
170
239
|
case 'layout-cast':
|
|
171
|
-
validateExpression(expression.base, span,
|
|
240
|
+
validateExpression(expression.base, span, symbols, diagnostics);
|
|
172
241
|
for (const part of expression.path) {
|
|
173
242
|
if (part.kind === 'index') {
|
|
174
|
-
validateExpression(part.expression, span,
|
|
243
|
+
validateExpression(part.expression, span, symbols, diagnostics);
|
|
175
244
|
}
|
|
176
245
|
}
|
|
177
246
|
return;
|
|
178
247
|
case 'number':
|
|
179
248
|
case 'current-location':
|
|
180
|
-
case 'type-size':
|
|
181
249
|
case 'sizeof':
|
|
182
250
|
case 'offset':
|
|
183
251
|
return;
|
|
252
|
+
case 'type-size':
|
|
253
|
+
if (expression.typeExpr.length === undefined) {
|
|
254
|
+
validateSymbolReference(expression.typeExpr.name, span, symbols, diagnostics);
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
184
257
|
}
|
|
185
258
|
}
|
|
186
|
-
function validateSymbolReference(name, referenceSpan,
|
|
187
|
-
const label = lookupLabel(
|
|
188
|
-
if (!label || label.public)
|
|
259
|
+
function validateSymbolReference(name, referenceSpan, symbols, diagnostics) {
|
|
260
|
+
const label = lookupLabel(symbols, name);
|
|
261
|
+
if (!label || label.duplicateName || label.public)
|
|
189
262
|
return;
|
|
190
263
|
if (referenceSpan.sourceUnit === label.definingSourceUnit)
|
|
191
264
|
return;
|
|
192
265
|
diagnostics.push(diagnostic(referenceSpan, `symbol "${name}" is private to ${label.definingSourceName}; export it with @${label.name} or keep the reference inside that file`));
|
|
193
266
|
}
|
|
194
|
-
function lookupLabel(
|
|
195
|
-
const direct = labels.get(name);
|
|
267
|
+
function lookupLabel(symbols, name) {
|
|
268
|
+
const direct = symbols.labels.get(name);
|
|
196
269
|
if (direct)
|
|
197
270
|
return direct;
|
|
271
|
+
if (symbols.exactSymbols.has(name))
|
|
272
|
+
return undefined;
|
|
198
273
|
const lowerName = name.toLowerCase();
|
|
199
|
-
for (const [key, label] of labels) {
|
|
274
|
+
for (const [key, label] of symbols.labels) {
|
|
200
275
|
if (key.toLowerCase() === lowerName)
|
|
201
276
|
return label;
|
|
202
277
|
}
|
|
@@ -57,6 +57,11 @@ function queueRegisterContractsArtifacts(writes, byKind, paths) {
|
|
|
57
57
|
writes.push(writeTextArtifact(paths.registerContractsInterface, iface.text));
|
|
58
58
|
registerContractsPath ??= paths.registerContractsInterface;
|
|
59
59
|
}
|
|
60
|
+
const inference = byKind.get('register-contracts-inference');
|
|
61
|
+
if (inference?.kind === 'register-contracts-inference') {
|
|
62
|
+
writes.push(writeTextArtifact(paths.registerContractsInference, inference.text));
|
|
63
|
+
registerContractsPath ??= paths.registerContractsInference;
|
|
64
|
+
}
|
|
60
65
|
return registerContractsPath;
|
|
61
66
|
}
|
|
62
67
|
function queueRegisterContractsAnnotationArtifacts(writes, byKind) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RegisterContractsMode } from '../register-contracts/types.js';
|
|
1
|
+
import type { RegisterContractsInferenceFormat, RegisterContractsMode, RegisterContractsReportFormat } from '../register-contracts/types.js';
|
|
2
2
|
import type { CaseStyleMode } from '../tooling/case-style.js';
|
|
3
3
|
import { cliUsage } from './usage.js';
|
|
4
4
|
export type CliExit = {
|
|
@@ -16,7 +16,12 @@ export type CliOptions = {
|
|
|
16
16
|
caseStyle: CaseStyleMode;
|
|
17
17
|
registerContracts: RegisterContractsMode;
|
|
18
18
|
emitRegisterReport: boolean;
|
|
19
|
+
registerContractsReportFormat: RegisterContractsReportFormat;
|
|
20
|
+
registerContractsBaseline: string | undefined;
|
|
21
|
+
registerContractsRatchet: boolean;
|
|
19
22
|
emitRegisterInterface: boolean;
|
|
23
|
+
emitRegisterInference: boolean;
|
|
24
|
+
registerContractsInferenceFormat: RegisterContractsInferenceFormat;
|
|
20
25
|
emitRegisterAnnotations: boolean;
|
|
21
26
|
fixRegisterContracts: boolean;
|
|
22
27
|
acceptRegisterOutputCandidates: string[];
|
|
@@ -38,6 +38,20 @@ const BOOLEAN_FLAG_ACTIONS = [
|
|
|
38
38
|
state.emitRegisterInterface = true;
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
|
+
{
|
|
42
|
+
flags: ['--reg-infer'],
|
|
43
|
+
apply: (state) => {
|
|
44
|
+
state.emitRegisterInference = true;
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
flags: ['--reg-ratchet'],
|
|
49
|
+
apply: (state) => {
|
|
50
|
+
state.registerContractsRatchet = true;
|
|
51
|
+
state.emitRegisterReport = true;
|
|
52
|
+
state.registerContractsReportFormat = 'json';
|
|
53
|
+
},
|
|
54
|
+
},
|
|
41
55
|
{
|
|
42
56
|
flags: ['--fix'],
|
|
43
57
|
apply: (state) => {
|
|
@@ -67,7 +81,12 @@ function createDefaultCliState() {
|
|
|
67
81
|
caseStyle: 'off',
|
|
68
82
|
registerContracts: 'off',
|
|
69
83
|
emitRegisterReport: false,
|
|
84
|
+
registerContractsReportFormat: 'text',
|
|
85
|
+
registerContractsBaseline: undefined,
|
|
86
|
+
registerContractsRatchet: false,
|
|
70
87
|
emitRegisterInterface: false,
|
|
88
|
+
emitRegisterInference: false,
|
|
89
|
+
registerContractsInferenceFormat: 'json',
|
|
71
90
|
emitRegisterAnnotations: false,
|
|
72
91
|
fixRegisterContracts: false,
|
|
73
92
|
acceptRegisterOutputCandidates: [],
|
|
@@ -199,6 +218,37 @@ function parseRegisterProfileArg(arg, argv, indexRef, state) {
|
|
|
199
218
|
state.registerContractsProfile = parsed.value;
|
|
200
219
|
return true;
|
|
201
220
|
}
|
|
221
|
+
function parseRegisterReportFormatArg(arg, argv, indexRef, state) {
|
|
222
|
+
const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--reg-report-format']);
|
|
223
|
+
if (!parsed)
|
|
224
|
+
return false;
|
|
225
|
+
if (parsed.value !== 'text' && parsed.value !== 'json') {
|
|
226
|
+
fail(`Unsupported ${parsed.flag} "${parsed.value}" (expected text|json)`);
|
|
227
|
+
}
|
|
228
|
+
state.emitRegisterReport = true;
|
|
229
|
+
state.registerContractsReportFormat = parsed.value;
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
function parseRegisterInferenceFormatArg(arg, argv, indexRef, state) {
|
|
233
|
+
const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--reg-infer-format']);
|
|
234
|
+
if (!parsed)
|
|
235
|
+
return false;
|
|
236
|
+
if (parsed.value !== 'json' && parsed.value !== 'markdown') {
|
|
237
|
+
fail(`Unsupported ${parsed.flag} "${parsed.value}" (expected json|markdown)`);
|
|
238
|
+
}
|
|
239
|
+
state.emitRegisterInference = true;
|
|
240
|
+
state.registerContractsInferenceFormat = parsed.value;
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
function parseRegisterBaselineArg(arg, argv, indexRef, state) {
|
|
244
|
+
const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--reg-baseline']);
|
|
245
|
+
if (!parsed)
|
|
246
|
+
return false;
|
|
247
|
+
state.registerContractsBaseline = parsed.value;
|
|
248
|
+
state.emitRegisterReport = true;
|
|
249
|
+
state.registerContractsReportFormat = 'json';
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
202
252
|
function parseRegisterInterfaceArg(arg, argv, indexRef, state) {
|
|
203
253
|
if (arg !== '--interface' && !arg.startsWith('--interface='))
|
|
204
254
|
return false;
|
|
@@ -264,7 +314,12 @@ function finalizeCliOptions(state) {
|
|
|
264
314
|
caseStyle: state.caseStyle,
|
|
265
315
|
registerContracts: state.registerContracts,
|
|
266
316
|
emitRegisterReport: state.emitRegisterReport,
|
|
317
|
+
registerContractsReportFormat: state.registerContractsBaseline !== undefined ? 'json' : state.registerContractsReportFormat,
|
|
318
|
+
registerContractsBaseline: state.registerContractsBaseline,
|
|
319
|
+
registerContractsRatchet: state.registerContractsRatchet,
|
|
267
320
|
emitRegisterInterface: state.emitRegisterInterface,
|
|
321
|
+
emitRegisterInference: state.emitRegisterInference,
|
|
322
|
+
registerContractsInferenceFormat: state.registerContractsInferenceFormat,
|
|
268
323
|
emitRegisterAnnotations: state.emitRegisterAnnotations,
|
|
269
324
|
fixRegisterContracts: state.fixRegisterContracts,
|
|
270
325
|
acceptRegisterOutputCandidates: state.acceptRegisterOutputCandidates,
|
|
@@ -299,6 +354,7 @@ function emitsRegisterContractsArtifact(state) {
|
|
|
299
354
|
state.registerContracts !== 'off',
|
|
300
355
|
state.emitRegisterReport,
|
|
301
356
|
state.emitRegisterInterface,
|
|
357
|
+
state.emitRegisterInference,
|
|
302
358
|
state.emitRegisterAnnotations,
|
|
303
359
|
state.fixRegisterContracts,
|
|
304
360
|
state.acceptRegisterOutputCandidates.length > 0,
|
|
@@ -324,6 +380,9 @@ const VALUE_ARG_PARSERS = [
|
|
|
324
380
|
(arg, { argv, indexRef, state }) => parseDirectiveAliasFileArg(arg, argv, indexRef, state),
|
|
325
381
|
(arg, { argv, indexRef, state }) => parseRegisterContractsArg(arg, argv, indexRef, state),
|
|
326
382
|
(arg, { argv, indexRef, state }) => parseRegisterProfileArg(arg, argv, indexRef, state),
|
|
383
|
+
(arg, { argv, indexRef, state }) => parseRegisterReportFormatArg(arg, argv, indexRef, state),
|
|
384
|
+
(arg, { argv, indexRef, state }) => parseRegisterInferenceFormatArg(arg, argv, indexRef, state),
|
|
385
|
+
(arg, { argv, indexRef, state }) => parseRegisterBaselineArg(arg, argv, indexRef, state),
|
|
327
386
|
(arg, { argv, indexRef, state }) => parseAcceptOutputArg(arg, argv, indexRef, state),
|
|
328
387
|
(arg, { argv, indexRef, state }) => parseRegisterInterfaceArg(arg, argv, indexRef, state),
|
|
329
388
|
(arg, { argv, indexRef, state }) => parseIncludeArg(arg, argv, indexRef, state),
|
package/dist/src/cli/run.js
CHANGED
|
@@ -19,11 +19,11 @@ export async function runCli(argv) {
|
|
|
19
19
|
}
|
|
20
20
|
if (sortedDiagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
|
|
21
21
|
if (compileResult.artifacts.length > 0) {
|
|
22
|
-
await writeArtifacts(base, compileResult.artifacts, parsed.outputType);
|
|
22
|
+
await writeArtifacts(base, compileResult.artifacts, parsed.outputType, parsed.registerContractsReportFormat);
|
|
23
23
|
}
|
|
24
24
|
return 1;
|
|
25
25
|
}
|
|
26
|
-
const primaryPath = await writeArtifacts(base, compileResult.artifacts, parsed.outputType);
|
|
26
|
+
const primaryPath = await writeArtifacts(base, compileResult.artifacts, parsed.outputType, parsed.registerContractsReportFormat);
|
|
27
27
|
if (primaryPath !== undefined) {
|
|
28
28
|
process.stdout.write(primaryPath);
|
|
29
29
|
}
|
package/dist/src/cli/usage.js
CHANGED
|
@@ -12,7 +12,12 @@ export function cliUsage() {
|
|
|
12
12
|
' --register-contracts <m> Register contracts mode: off|audit|warn|error|strict',
|
|
13
13
|
' --rc <m> Register contracts mode alias for --register-contracts',
|
|
14
14
|
' --reg-report Emit register contracts report artifact',
|
|
15
|
+
' --reg-report-format <f> Report format: text|json (default: text)',
|
|
16
|
+
' --reg-baseline <f> Compare against a JSON register contracts report',
|
|
17
|
+
' --reg-ratchet Fail if register contract findings increase',
|
|
15
18
|
' --reg-interface Emit inferred register contracts interface (.asmi)',
|
|
19
|
+
' --reg-infer Emit inferred register contracts review artifact',
|
|
20
|
+
' --reg-infer-format <f> Inference format: json|markdown (default: json)',
|
|
16
21
|
' --contracts Rewrite source with inferred register contracts',
|
|
17
22
|
' --fix Enable contract rewrite and conservative fixes',
|
|
18
23
|
' --accept-out <x> Accept register contracts output candidates',
|
|
@@ -17,5 +17,5 @@ export declare function compareDiagnosticsForCli(a: {
|
|
|
17
17
|
message: string;
|
|
18
18
|
}): number;
|
|
19
19
|
export declare function artifactBase(entryFile: string, outputType: 'hex' | 'bin', outputPath?: string): string;
|
|
20
|
-
export declare function writeArtifacts(base: string, artifacts: readonly Artifact[], outputType: 'hex' | 'bin'): Promise<string | undefined>;
|
|
20
|
+
export declare function writeArtifacts(base: string, artifacts: readonly Artifact[], outputType: 'hex' | 'bin', registerContractsReportFormat?: 'text' | 'json'): Promise<string | undefined>;
|
|
21
21
|
export declare function buildCompileOptions(parsed: CliOptions, base: string): CompileNextFunctionOptions;
|