implements PLN-0122
This commit is contained in:
parent
cf14039077
commit
f42c83bdca
@ -6,7 +6,7 @@
|
||||
{"type":"discussion","id":"DSC-0061","status":"open","ticket":"multi-frontend-sdk-canonical-definition","title":"Auditoria e centralizacao gradual da definicao canonica do SDK","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","sdk","stdlib","hostcalls","intrinsics","multi-frontend"],"agendas":[{"id":"AGD-0064","file":"AGD-0064-multi-frontend-sdk-canonical-definition.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0060","status":"open","ticket":"multi-frontend-validation-boundaries","title":"Separar validacoes de linguagem e validacoes de plataforma","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","backend","validation","multi-frontend"],"agendas":[{"id":"AGD-0063","file":"AGD-0063-multi-frontend-validation-boundaries.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0059","status":"open","ticket":"multi-frontend-common-lifecycle","title":"Extrair lifecycle comum das responsabilidades do frontend PBS","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","lifecycle","backend","multi-frontend"],"agendas":[{"id":"AGD-0062","file":"AGD-0062-multi-frontend-common-lifecycle.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0058","status":"in_progress","ticket":"multi-frontend-serializable-ir","title":"Manter a IR comum serializavel por design","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","ir","backend","serialization","multi-frontend"],"agendas":[{"id":"AGD-0061","file":"AGD-0061-multi-frontend-serializable-ir.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0044","file":"DEC-0044-serializable-common-ir-boundary.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0061"}],"plans":[{"id":"PLN-0119","file":"PLN-0119-document-irbackend-serializability-invariants.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0120","file":"PLN-0120-audit-public-irbackend-contract-shape.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0121","file":"PLN-0121-add-serializable-irbackend-reflection-guardrails.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0122","file":"PLN-0122-prove-deterministic-irbackend-ordering.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0123","file":"PLN-0123-fix-concrete-irbackend-serialization-leaks.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0058","status":"in_progress","ticket":"multi-frontend-serializable-ir","title":"Manter a IR comum serializavel por design","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","ir","backend","serialization","multi-frontend"],"agendas":[{"id":"AGD-0061","file":"AGD-0061-multi-frontend-serializable-ir.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0044","file":"DEC-0044-serializable-common-ir-boundary.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0061"}],"plans":[{"id":"PLN-0119","file":"PLN-0119-document-irbackend-serializability-invariants.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0120","file":"PLN-0120-audit-public-irbackend-contract-shape.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0121","file":"PLN-0121-add-serializable-irbackend-reflection-guardrails.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0122","file":"PLN-0122-prove-deterministic-irbackend-ordering.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]},{"id":"PLN-0123","file":"PLN-0123-fix-concrete-irbackend-serialization-leaks.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0044"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0057","status":"done","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0059","file":"discussion/lessons/DSC-0057-multi-frontend-frontend-backend-contract/LSN-0059-common-irbackend-handoff-and-backend-guardrails.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
||||
{"type":"discussion","id":"DSC-0056","status":"done","ticket":"multi-frontend-remove-pbs-branches","title":"Generalizar o contrato LSP/editorial para frontends","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0058","file":"discussion/lessons/DSC-0056-multi-frontend-remove-pbs-branches/LSN-0058-generic-frontend-editorial-contract-for-lsp.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
||||
{"type":"discussion","id":"DSC-0055","status":"done","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0056","file":"discussion/lessons/DSC-0055-multi-frontend-compiler-vs-language-services/LSN-0056-compile-first-frontends-with-optional-editorial-capabilities.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0122
|
||||
ticket: multi-frontend-serializable-ir
|
||||
title: Prove deterministic IRBackend ordering
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-07-15
|
||||
ref_decisions: [DEC-0044]
|
||||
tags: [compiler, compiler-general, ir, backend, serialization, multi-frontend]
|
||||
|
||||
@ -43,7 +43,7 @@ to concrete positive/negative test evidence and current status.
|
||||
| G20-4.1.1 | `IRBackend` MUST be the common frontend-to-backend executable handoff. | `LowerToIRVMServiceTest#lowerMustAcceptManuallyConstructedCommonIRBackend` | N/A | pass | Direct backend lowering from manually constructed common IRBackend proves PBS parser/frontend services are not required. |
|
||||
| G20-4.1.2 | Common backend code MUST NOT depend on `p.studio.compiler.pbs`. | `CommonBackendArchitectureTest#commonBackendMustNotImportPbsFrontendPackages` | N/A | pass | Source-level architectural guard rejects PBS frontend imports in common backend packages. |
|
||||
| G20-4.1.3 | Public `IRBackend` contract MUST NOT expose PBS AST, token, parser, semantic, or editorial types. | `IRBackendExecutableContractTest#publicIRBackendContractMustNotExposePbsTypes` | N/A | pass | Reflection guard covers public contract fields, constructors, methods, and record components. |
|
||||
| G20-4.2.1 | Public `IRBackend` contract types MUST remain modelable as an acyclic, deterministic data graph. | `IRBackendExecutableContractTest#publicIRBackendContractMustExposeOnlySerializableShapes`; planned deterministic ordering tests from `PLN-0122` | N/A | partial | Public shape guardrails exist; deterministic ordering evidence is still planned before this row can move to pass. |
|
||||
| G20-4.2.1 | Public `IRBackend` contract types MUST remain modelable as an acyclic, deterministic data graph. | `IRBackendExecutableContractTest#publicIRBackendContractMustExposeOnlySerializableShapes`; `IRBackendExecutableContractTest#aggregatorMustEmitDeterministicOrderedPublicContractShape` | N/A | pass | Covers public shape guardrails, generic type arguments, ordered aggregate emission, table remapping, and capability de-duplication order. |
|
||||
| G20-4.2.2 | Public `IRBackend` contract types MUST NOT expose callbacks, services, mutable/unordered collection contracts, frontend-owned objects, process-dependent lazy values, cyclic public references, or wire-format commitments. | `IRBackendExecutableContractTest#publicIRBackendContractMustExposeOnlySerializableShapes` | N/A | pass | Reflection guard covers public contract fields, constructors, methods, record components, and generic type arguments. |
|
||||
| G20-4.2.3 | Public `IRBackend` cross-object references MUST use explicit ids or stable symbolic keys. | Planned: public contract audit from `PLN-0120`; targeted leak fixes from `PLN-0123` when needed | N/A | missing | Existing `FileId`, `ModuleId`, `CallableId`, and `IntrinsicId` are expected allowed values when table scope is deterministic. |
|
||||
| G20-6.2 | `IRVM_EXT` MUST declare structural metadata (`pops/pushes/is_branch/is_terminator`). | `IRVMValidatorTest#validateMustApplyStructuralMetadataForCustomInternalExtension`; `IRVMValidatorTest#validateMustRejectCustomInternalExtensionWhenStructuralMetadataUnderflowsStack`; `IRVMOp` record contract (`pops/pushes/branch/terminator/internal`) | N/A | pass | Dedicated extension fixtures now assert structural metadata is consumed by validation behavior. |
|
||||
|
||||
@ -559,6 +559,208 @@ class IRBackendExecutableContractTest {
|
||||
assertEquals(1, backend.getCallableSignatures().get(1).moduleId().getIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
void aggregatorMustEmitDeterministicOrderedPublicContractShape() {
|
||||
final var first = emitDeterministicAggregateFixture();
|
||||
final var second = emitDeterministicAggregateFixture();
|
||||
|
||||
assertEquals(
|
||||
List.of("alpha", "beta"),
|
||||
first.getFunctions().asList().stream().map(IRFunction::name).toList());
|
||||
assertEquals(
|
||||
List.of("frameWrapper", "initWrapper"),
|
||||
first.getSyntheticFunctions().asList().stream().map(IRSyntheticFunction::callableName).toList());
|
||||
assertEquals(
|
||||
List.of("state", "timer"),
|
||||
first.getGlobals().asList().stream().map(IRGlobal::name).toList());
|
||||
assertEquals(
|
||||
List.of("frame", "init"),
|
||||
first.getExecutableFunctions().asList().stream().map(IRBackendExecutableFunction::callableName).toList());
|
||||
assertEquals(
|
||||
List.of("app:alpha", "app:beta"),
|
||||
first.getModulePool().asList().stream()
|
||||
.map(module -> module.project() + ":" + String.join(".", module.pathSegments().asList()))
|
||||
.toList());
|
||||
assertEquals(
|
||||
List.of("frame", "helper", "init"),
|
||||
first.getCallableSignatures().asList().stream().map(CallableSignatureRef::callableName).toList());
|
||||
assertEquals(
|
||||
List.of("core.draw", "core.color.pack"),
|
||||
first.getIntrinsicPool().asList().stream().map(IntrinsicReference::canonicalName).toList());
|
||||
assertEquals(
|
||||
List.of("draw", "play"),
|
||||
first.getReservedMetadata().hostMethodBindings().asList().stream()
|
||||
.map(IRReservedMetadata.HostMethodBinding::abiMethod)
|
||||
.toList());
|
||||
assertEquals(
|
||||
List.of("Sprite", "Sound"),
|
||||
first.getReservedMetadata().builtinTypeSurfaces().asList().stream()
|
||||
.map(IRReservedMetadata.BuiltinTypeSurface::sourceTypeName)
|
||||
.toList());
|
||||
assertEquals(
|
||||
List.of("WHITE", "BLACK"),
|
||||
first.getReservedMetadata().builtinConstSurfaces().asList().stream()
|
||||
.map(IRReservedMetadata.BuiltinConstSurface::sourceConstName)
|
||||
.toList());
|
||||
assertEquals(
|
||||
List.of("gfx.draw", "audio.play", "input.read"),
|
||||
first.getReservedMetadata().requiredCapabilities().asList());
|
||||
|
||||
assertEquals(first.toString(), second.toString());
|
||||
assertEquals(
|
||||
first.getReservedMetadata().requiredCapabilities().asList(),
|
||||
second.getReservedMetadata().requiredCapabilities().asList());
|
||||
assertEquals(
|
||||
first.getExecutableFunctions().get(0).moduleId(),
|
||||
second.getExecutableFunctions().get(0).moduleId());
|
||||
assertEquals(
|
||||
first.getExecutableFunctions().get(1).callableId(),
|
||||
second.getExecutableFunctions().get(1).callableId());
|
||||
}
|
||||
|
||||
private static IRBackend emitDeterministicAggregateFixture() {
|
||||
final var aggregator = IRBackend.aggregator();
|
||||
aggregator.merge(new IRBackendFile(
|
||||
new FileId(10),
|
||||
ReadOnlyList.from(new IRFunction(new FileId(10), "alpha", 0, false, new Span(new FileId(10), 0, 5))),
|
||||
ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"frameWrapper",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(10), "frame", new Span(new FileId(10), 0, 5)))),
|
||||
ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(10),
|
||||
new ModuleId(0),
|
||||
"state",
|
||||
"int",
|
||||
new Span(new FileId(10), 1, 2))),
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction(
|
||||
new FileId(10),
|
||||
new ModuleId(0),
|
||||
"frame",
|
||||
new CallableId(0),
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
new ModuleId(0),
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata("core.draw", 1, new IntrinsicId(0)),
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
new IRReservedMetadata(
|
||||
ReadOnlyList.from(new IRReservedMetadata.HostMethodBinding(
|
||||
"Graphics",
|
||||
"draw",
|
||||
"gfx",
|
||||
"draw",
|
||||
1,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
"gfx.draw",
|
||||
Span.none())),
|
||||
ReadOnlyList.from(new IRReservedMetadata.BuiltinTypeSurface(
|
||||
"Sprite",
|
||||
"core.Sprite",
|
||||
1,
|
||||
ReadOnlyList.from(new IRReservedMetadata.BuiltinFieldSurface(
|
||||
"x",
|
||||
"int",
|
||||
0,
|
||||
Span.none())),
|
||||
ReadOnlyList.empty(),
|
||||
Span.none())),
|
||||
ReadOnlyList.from(new IRReservedMetadata.BuiltinConstSurface(
|
||||
"WHITE",
|
||||
"color",
|
||||
"core.color.white",
|
||||
1,
|
||||
Span.none())),
|
||||
ReadOnlyList.from("gfx.draw", "audio.play")),
|
||||
ReadOnlyList.from(new ModuleReference("app", ReadOnlyList.from("alpha"))),
|
||||
ReadOnlyList.from(
|
||||
new CallableSignatureRef(new ModuleId(0), "frame", 0, "() -> unit"),
|
||||
new CallableSignatureRef(new ModuleId(0), "helper", 0, "() -> unit")),
|
||||
ReadOnlyList.from(new IntrinsicReference("core.draw", 1))));
|
||||
aggregator.merge(new IRBackendFile(
|
||||
new FileId(20),
|
||||
ReadOnlyList.from(new IRFunction(new FileId(20), "beta", 1, true, new Span(new FileId(20), 0, 7))),
|
||||
ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"initWrapper",
|
||||
IRSyntheticCallableKind.PROJECT_INIT,
|
||||
new IRSyntheticOrigin(new FileId(20), "init", new Span(new FileId(20), 0, 7)))),
|
||||
ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(20),
|
||||
new ModuleId(0),
|
||||
"timer",
|
||||
"int",
|
||||
new Span(new FileId(20), 1, 3))),
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction(
|
||||
new FileId(20),
|
||||
new ModuleId(0),
|
||||
"init",
|
||||
new CallableId(0),
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
new ModuleId(0),
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata("core.color.pack", 1, new IntrinsicId(0)),
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
new IRReservedMetadata(
|
||||
ReadOnlyList.from(new IRReservedMetadata.HostMethodBinding(
|
||||
"Audio",
|
||||
"play",
|
||||
"audio",
|
||||
"play",
|
||||
1,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
"audio.play",
|
||||
Span.none())),
|
||||
ReadOnlyList.from(new IRReservedMetadata.BuiltinTypeSurface(
|
||||
"Sound",
|
||||
"core.Sound",
|
||||
1,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.from(new IRReservedMetadata.IntrinsicSurface(
|
||||
"volume",
|
||||
"core.sound.volume",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
ReadOnlyList.from(new IRReservedMetadata.BuiltinConstSurface(
|
||||
"BLACK",
|
||||
"color",
|
||||
"core.color.black",
|
||||
1,
|
||||
Span.none())),
|
||||
ReadOnlyList.from("gfx.draw", "input.read")),
|
||||
ReadOnlyList.from(new ModuleReference("app", ReadOnlyList.from("beta"))),
|
||||
ReadOnlyList.from(new CallableSignatureRef(new ModuleId(0), "init", 0, "() -> unit")),
|
||||
ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1))));
|
||||
return aggregator.emit();
|
||||
}
|
||||
|
||||
private static void inspectType(
|
||||
final String owner,
|
||||
final Type type,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user