mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 18:05:45 +00:00
fix(javascript)!: preserve property paths and uncertain slot presence
Use one explicit object/array slot model and JSON-pointer property identity across mutation analysis, return projection, and semantic graphs. Require observed presence for discriminants and resolve writes only through retained object or array receivers. BREAKING CHANGE: projected return fields require explicit presence and semantic object operations carry full property paths.
This commit is contained in:
1 parent
63bf91405c
commit
475a0f84da
23 files changed
+831
-560
No files matched your search
@@ -1,4 +1,8 @@
|
||||
import type { JsonValue } from "../../domain/jsonValue.js";
|
||||
import {
|
||||
semanticContainer,
|
||||
semanticPropertyPointer,
|
||||
type JavaScriptSemanticSlot,
|
||||
} from "../../domain/javascript/javascriptSemanticSlots.js";
|
||||
import type {
|
||||
JavaScriptSemanticBinding,
|
||||
JavaScriptSemanticValue,
|
||||
@@ -24,7 +28,7 @@ export const projectSemanticValues = (
|
||||
binding,
|
||||
value: binding.value,
|
||||
target: bindingNode,
|
||||
role: "binding",
|
||||
path: [],
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -34,11 +38,13 @@ interface ValueProjectionInput {
|
||||
readonly binding: JavaScriptSemanticBinding;
|
||||
readonly value: JavaScriptSemanticValue;
|
||||
readonly target: ReturnType<typeof semanticPropertySlot>;
|
||||
readonly role: string;
|
||||
readonly path: readonly string[];
|
||||
}
|
||||
|
||||
const projectValue = (input: ValueProjectionInput): void => {
|
||||
const { context, binding, value, target, role } = input;
|
||||
const { context, binding, value, target, path } = input;
|
||||
const role =
|
||||
path.length === 0 ? "binding" : `property:${semanticPropertyPointer(path)}`;
|
||||
if (target === null) return;
|
||||
if (value.status === "literal") {
|
||||
const literal = addLiteralNode(context, binding, value.value, role);
|
||||
@@ -58,31 +64,33 @@ const projectValue = (input: ValueProjectionInput): void => {
|
||||
resolution: "candidate",
|
||||
});
|
||||
}
|
||||
else if (value.status === "object")
|
||||
for (const property of value.properties) {
|
||||
else if (value.status === "object" || value.status === "array") {
|
||||
const container = semanticContainer(value);
|
||||
for (const property of container.slots) {
|
||||
const propertyPath = [...path, property.name];
|
||||
const slot = semanticPropertySlot(
|
||||
context,
|
||||
binding.bindingId,
|
||||
property.name,
|
||||
propertyPath,
|
||||
property,
|
||||
);
|
||||
if (property.presence === "absent") continue;
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: target,
|
||||
target: slot,
|
||||
relation: "writes-property",
|
||||
resolution:
|
||||
value.unknownProperties || property.presence === "unknown-coverage"
|
||||
? "candidate"
|
||||
: "resolved",
|
||||
});
|
||||
projectValue({
|
||||
context,
|
||||
binding,
|
||||
value: property.value,
|
||||
target: slot,
|
||||
role: `property:${property.name}`,
|
||||
resolution: property.presence === "present" ? "resolved" : "candidate",
|
||||
});
|
||||
if (property.presence === "present")
|
||||
projectValue({
|
||||
context,
|
||||
binding,
|
||||
value: property.value,
|
||||
target: slot,
|
||||
path: propertyPath,
|
||||
});
|
||||
}
|
||||
else if (value.status === "unknown" && value.resourceLimit !== undefined) {
|
||||
} else if (value.status === "unknown" && value.resourceLimit !== undefined) {
|
||||
const location = binding.definitions[0]?.location ?? null;
|
||||
const evidence = observedSemanticEvidence(context.file, location);
|
||||
const isPropertyValue = role.startsWith("property:");
|
||||
@@ -123,21 +131,27 @@ const addLiteralNode = (
|
||||
properties: { value },
|
||||
});
|
||||
|
||||
/** Create one canonical property slot for a binding/property identity. */
|
||||
/** Create one canonical slot for a root binding and exact static property path. */
|
||||
export const semanticPropertySlot = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
objectBindingId: string,
|
||||
name: string,
|
||||
path: readonly string[],
|
||||
fact: JavaScriptSemanticSlot,
|
||||
) =>
|
||||
retainSemanticGraphNode(context.state, context.file, {
|
||||
kind: "property-slot",
|
||||
roleKey: `property:${objectBindingId}:${name}`,
|
||||
roleKey: `property:${JSON.stringify([objectBindingId, path])}`,
|
||||
location: null,
|
||||
label: name,
|
||||
label: fact.name,
|
||||
functionNodeId:
|
||||
context.bindingNodes.get(objectBindingId)?.function_node_id ?? null,
|
||||
properties: {
|
||||
name,
|
||||
name: fact.name,
|
||||
property_path: [...path],
|
||||
property_pointer: semanticPropertyPointer(path),
|
||||
object_binding_id: objectBindingId,
|
||||
} satisfies Readonly<Record<string, JsonValue>>,
|
||||
presence: fact.presence,
|
||||
value_status: fact.value.status,
|
||||
value_coverage: semanticContainer(fact.value)?.coverage ?? null,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user