rea/src/application/JavaScriptReturnShapeProjec...

154 lines
5.2 KiB
TypeScript

import { jsonObjectSchema, jsonValueSchema } from "../domain/jsonValue.js";
import type {
JavaScriptSemanticIr,
JavaScriptSemanticCallable,
JavaScriptSemanticModuleLink,
} from "../domain/javascriptSemanticIr.js";
import { flattenSemanticReturnValue } from "../domain/javascriptSemanticReturns.js";
import type { JavaScriptSourceRange } from "../domain/javascriptStaticAnalysisTypes.js";
import type { JavaScriptArtifactGraphCoverage } from "./JavaScriptArtifactGraphContext.js";
import { partialReconstructionCoverage } from "./JavaScriptArtifactGraphEvidence.js";
const MAX_PROJECTED_RETURN_SHAPES = 32;
const MAX_PROJECTED_RETURN_FIELDS = 64;
const MAX_PROJECTED_PROPERTY_COVERAGE = 64;
export interface JavaScriptReturnShapeProjection {
readonly properties: ReturnType<typeof jsonObjectSchema.parse>;
readonly range: JavaScriptSourceRange;
readonly coverage: JavaScriptArtifactGraphCoverage;
readonly limitations: readonly string[];
}
/** Project one exact export/callable link into shallow bounded graph values. */
export const projectJavaScriptExportReturnShapes = (input: {
readonly ir: JavaScriptSemanticIr;
readonly link: JavaScriptSemanticModuleLink;
readonly modulePath: string;
readonly baseCoverage: JavaScriptArtifactGraphCoverage;
}): JavaScriptReturnShapeProjection | null => {
if (input.link.callableId === null || input.link.exportedName === null)
return null;
const callable = input.ir.callables.find(
({ callableId }) => callableId === input.link.callableId,
);
if (callable === undefined) return null;
const projection = projectReturnSites(callable);
const projectionOmitted =
projection.omittedSites +
projection.omittedFields +
projection.omittedCoverage;
const semanticOmitted = callable.returnCoverage.omittedCount;
const complete =
input.baseCoverage.status === "complete" &&
callable.returnCoverage.status === "complete" &&
projectionOmitted === 0;
const coverage = complete
? input.baseCoverage
: partialReconstructionCoverage(
[
...input.baseCoverage.limits,
{
name: "return-shape-sites",
value: MAX_PROJECTED_RETURN_SHAPES,
unit: "items",
},
{
name: "return-shape-fields",
value: MAX_PROJECTED_RETURN_FIELDS,
unit: "items",
},
{
name: "return-shape-property-coverage",
value: MAX_PROJECTED_PROPERTY_COVERAGE,
unit: "items",
},
],
semanticOmitted === null ? null : semanticOmitted + projectionOmitted,
callable.returnCoverage.status === "truncated" || projectionOmitted > 0,
);
const limitations = [
...input.ir.limitations,
"Return shapes are inferred from inert syntax and do not prove runtime behavior.",
...(callable.returnSites.length === 0
? [
"No direct return value was retained; runtime return behavior remains unknown.",
]
: []),
...(projectionOmitted > 0
? ["Return-shape graph projection reached explicit retention limits."]
: []),
];
return {
properties: jsonObjectSchema.parse({
semantic_role: "export-return-shapes",
module_path: input.modulePath,
exported_name: input.link.exportedName,
callable_id: callable.callableId,
callable_kind: callable.kind,
static_return_shapes: projection.shapes,
return_shape_coverage: {
status: callable.returnCoverage.status,
retained_return_sites: projection.shapes.length,
omitted_return_sites:
semanticOmitted === null
? null
: semanticOmitted + projection.omittedSites,
omitted_fields: projection.omittedFields,
omitted_property_coverage: projection.omittedCoverage,
projection_complete: projectionOmitted === 0,
},
}),
range: callable.location,
coverage,
limitations,
};
};
const projectReturnSites = (callable: JavaScriptSemanticCallable) => {
let retainedFields = 0;
let retainedCoverage = 0;
let omittedFields = 0;
let omittedCoverage = 0;
const retainedSites = callable.returnSites.slice(
0,
MAX_PROJECTED_RETURN_SHAPES,
);
const shapes = retainedSites.map((site) => {
const flattened = flattenSemanticReturnValue(site.value);
const remainingFields = Math.max(
0,
MAX_PROJECTED_RETURN_FIELDS - retainedFields,
);
const fields = flattened.fields.slice(0, remainingFields).map((field) => ({
...field,
value: jsonValueSchema.parse(field.value),
}));
retainedFields += fields.length;
omittedFields += flattened.fields.length - fields.length;
const remainingCoverage = Math.max(
0,
MAX_PROJECTED_PROPERTY_COVERAGE - retainedCoverage,
);
const propertyCoverage = flattened.propertyCoverage.slice(
0,
remainingCoverage,
);
retainedCoverage += propertyCoverage.length;
omittedCoverage +=
flattened.propertyCoverage.length - propertyCoverage.length;
return {
source_range: site.location,
value_status: site.value.status,
fields,
property_coverage: propertyCoverage,
};
});
return {
shapes,
omittedSites: callable.returnSites.length - retainedSites.length,
omittedFields,
omittedCoverage,
};
};