diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java index 3e29f97a..6e16aa13 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java @@ -85,13 +85,14 @@ class PbsGateUSdkInterfaceConformanceTest { tempDir.resolve("gate-u-reserved-import-positive"), """ import { Color } from @core:color; + import { Composer } from @sdk:composer; import { Gfx } from @sdk:gfx; import { Input, InputPad, InputButton } from @sdk:input; import { Assets } from @sdk:asset; import { Log } from @sdk:log; declare contract Renderer { - fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void; + fn render(composer: Composer, gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void; } """, "pub contract Renderer;", @@ -115,6 +116,11 @@ class PbsGateUSdkInterfaceConformanceTest { && h.abiModule().equals("gfx") && h.abiMethod().equals("clear") && h.abiVersion() == 1)); + assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowComposer") + && h.abiModule().equals("composer") + && h.abiMethod().equals("emit_sprite") + && h.abiVersion() == 1)); assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream() .anyMatch(h -> h.ownerName().equals("LowLog") && h.abiModule().equals("log") diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java index ec9fa894..cac3a2ea 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java @@ -1006,8 +1006,72 @@ class PBSFrontendPhaseServiceTest { } @Test - void shouldLowerSdkGfxSetSpriteFacadeUsingBankIdContract() throws IOException { - final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-set-sprite"); + void shouldLowerSdkComposerEmitSpriteFacadeUsingCanonicalComposerContract() throws IOException { + final var projectRoot = tempDir.resolve("project-bootstrap-sdk-composer-emit-sprite"); + final var sourceRoot = projectRoot.resolve("src"); + final var modulePath = sourceRoot.resolve("app"); + Files.createDirectories(modulePath); + + final var sourceFile = modulePath.resolve("source.pbs"); + final var modBarrel = modulePath.resolve("mod.barrel"); + Files.writeString(sourceFile, """ + import { Composer } from @sdk:composer; + + fn render() -> int + { + return Composer.emit_sprite(7, 3, 12, 18, 0, 2, false, true, 1); + } + + [Frame] + fn frame() -> void + { + render(); + return; + } + """); + Files.writeString(modBarrel, """ + pub fn render() -> int; + pub fn frame() -> void; + """); + + final var projectTable = new ProjectTable(); + final var fileTable = new FileTable(1); + final var projectId = projectTable.register(ProjectDescriptor.builder() + .rootPath(projectRoot) + .name("app") + .version("1.0.0") + .sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot))) + .build()); + + registerFile(projectId, projectRoot, sourceFile, fileTable); + registerFile(projectId, projectRoot, modBarrel, fileTable); + + final var ctx = new FrontendPhaseContext( + projectTable, + fileTable, + new BuildStack(ReadOnlyList.wrap(List.of(projectId))), + 1); + final var diagnostics = DiagnosticSink.empty(); + + final var irBackend = new PBSFrontendPhaseService().compile( + ctx, + diagnostics, + LogAggregator.empty(), + BuildingIssueSink.empty()); + + assertTrue(diagnostics.stream().noneMatch(d -> + d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name()))); + assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function -> "frame".equals(function.callableName()))); + assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream() + .anyMatch(h -> h.ownerName().equals("LowComposer") + && h.sourceMethodName().equals("emit_sprite") + && h.abiModule().equals("composer") + && h.abiMethod().equals("emit_sprite"))); + } + + @Test + void shouldRejectLegacyGfxSetSpriteAfterComposerMigration() throws IOException { + final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-set-sprite-removed"); final var sourceRoot = projectRoot.resolve("src"); final var modulePath = sourceRoot.resolve("app"); Files.createDirectories(modulePath); @@ -1059,10 +1123,10 @@ class PBSFrontendPhaseServiceTest { LogAggregator.empty(), BuildingIssueSink.empty()); - assertTrue(diagnostics.stream().noneMatch(d -> - d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name()))); - assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function -> "frame".equals(function.callableName()))); - assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream() + assertTrue(diagnostics.stream().anyMatch(d -> + d.getCode().equals(PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name()) + || d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name()))); + assertFalse(irBackend.getReservedMetadata().hostMethodBindings().stream() .anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("set_sprite"))); }