requires explicit frame markers for executable PBS projects
This commit is contained in:
parent
459f5e3b87
commit
14eaa0e0c0
@ -152,9 +152,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
entryPointModuleId = wrapperNamesByModule.keySet().iterator().next();
|
||||
entryPointCallableName = wrapperNamesByModule.get(entryPointModuleId);
|
||||
} else {
|
||||
final var legacyEntrypoint = resolveLegacyEntrypoint(baseBackend);
|
||||
entryPointModuleId = legacyEntrypoint.moduleId();
|
||||
entryPointCallableName = legacyEntrypoint.callableName();
|
||||
entryPointModuleId = baseBackend.getEntryPointModuleId();
|
||||
entryPointCallableName = baseBackend.getEntryPointCallableName();
|
||||
}
|
||||
|
||||
final var sortedModuleIds = new ArrayList<ModuleId>();
|
||||
@ -397,19 +396,6 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
private LegacyEntrypoint resolveLegacyEntrypoint(final IRBackend backend) {
|
||||
for (final var candidateName : List.of("frame", "main")) {
|
||||
final var candidates = backend.getExecutableFunctions().stream()
|
||||
.filter(function -> candidateName.equals(function.callableName()))
|
||||
.filter(function -> function.moduleId() != null && !function.moduleId().isNone())
|
||||
.toList();
|
||||
if (candidates.size() == 1) {
|
||||
return new LegacyEntrypoint(candidates.getFirst().moduleId(), candidateName);
|
||||
}
|
||||
}
|
||||
return new LegacyEntrypoint(backend.getEntryPointModuleId(), backend.getEntryPointCallableName());
|
||||
}
|
||||
|
||||
private Set<ModuleId> blockedModulesByDependency(
|
||||
final Set<ModuleId> failedModuleIds,
|
||||
final Map<ModuleId, Set<ModuleId>> dependenciesByModule) {
|
||||
@ -440,9 +426,4 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
ModuleId moduleId,
|
||||
p.studio.compiler.models.IRBackendFile irBackendFile) {
|
||||
}
|
||||
|
||||
private record LegacyEntrypoint(
|
||||
ModuleId moduleId,
|
||||
String callableName) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,34 +13,31 @@ final class PbsProjectLifecycleSemanticsValidator {
|
||||
final java.util.List<PbsParsedSourceFile> parsedSourceFiles,
|
||||
final DiagnosticSink diagnostics) {
|
||||
final var frames = new ArrayList<PbsAst.FunctionDecl>();
|
||||
var sawLifecycleMarker = false;
|
||||
var sawExecutableSource = false;
|
||||
|
||||
for (final var parsedSource : parsedSourceFiles) {
|
||||
if (parsedSource.sourceKind() == SourceKind.SDK_INTERFACE) {
|
||||
continue;
|
||||
}
|
||||
sawExecutableSource = true;
|
||||
for (final var topDecl : parsedSource.ast().topDecls()) {
|
||||
if (!(topDecl instanceof PbsAst.FunctionDecl functionDecl)) {
|
||||
continue;
|
||||
}
|
||||
if (functionDecl.lifecycleMarker() == PbsAst.LifecycleMarker.NONE) {
|
||||
continue;
|
||||
}
|
||||
sawLifecycleMarker = true;
|
||||
if (functionDecl.lifecycleMarker() == PbsAst.LifecycleMarker.FRAME) {
|
||||
frames.add(functionDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sawLifecycleMarker) {
|
||||
if (!sawExecutableSource) {
|
||||
return;
|
||||
}
|
||||
if (frames.isEmpty()) {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(
|
||||
diagnostics,
|
||||
PbsSemanticsErrors.E_SEM_MISSING_PROJECT_FRAME.name(),
|
||||
"Lifecycle-marked executable sources must declare exactly one [Frame] function",
|
||||
"Executable projects must declare exactly one [Frame] function",
|
||||
parsedSourceFiles.getFirst().ast().span());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -162,8 +162,17 @@ class PBSFrontendPhaseServiceTest {
|
||||
fn caller() -> int {
|
||||
return target();
|
||||
}
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void {
|
||||
caller();
|
||||
return;
|
||||
}
|
||||
""");
|
||||
Files.writeString(barrelB, """
|
||||
pub fn caller() -> int;
|
||||
pub fn frame() -> void;
|
||||
""");
|
||||
Files.writeString(barrelB, "pub fn caller() -> int;");
|
||||
|
||||
final var projectTable = new ProjectTable();
|
||||
final var fileTable = new FileTable(1);
|
||||
@ -221,11 +230,12 @@ class PBSFrontendPhaseServiceTest {
|
||||
final var sourceFile = modulePath.resolve("source.pbs");
|
||||
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||
Files.writeString(sourceFile, """
|
||||
fn run() -> int { return 1; }
|
||||
[Frame]
|
||||
fn frame() -> void { return; }
|
||||
fn sum(a: int, b: int) -> int { return a + b; }
|
||||
""");
|
||||
Files.writeString(modBarrel, """
|
||||
pub fn run() -> int;
|
||||
pub fn frame() -> void;
|
||||
pub fn sum(a: int, b: int) -> int;
|
||||
""");
|
||||
|
||||
@ -255,7 +265,51 @@ class PBSFrontendPhaseServiceTest {
|
||||
|
||||
assertTrue(diagnostics.isEmpty());
|
||||
assertEquals(2, irBackend.getFunctions().size());
|
||||
assertEquals(2, irBackend.getExecutableFunctions().size());
|
||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function -> "frame".equals(function.callableName())));
|
||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
|
||||
function.callableName().startsWith("__pbs.frame_wrapper$m")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRequireExplicitFrameMarkerForExecutableProjects() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-missing-frame-marker");
|
||||
final var sourceRoot = projectRoot.resolve("src");
|
||||
Files.createDirectories(sourceRoot);
|
||||
|
||||
final var sourceFile = sourceRoot.resolve("main.pbs");
|
||||
final var modBarrel = sourceRoot.resolve("mod.barrel");
|
||||
Files.writeString(sourceFile, """
|
||||
fn frame() -> void { return; }
|
||||
""");
|
||||
Files.writeString(modBarrel, "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("main")
|
||||
.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))));
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
|
||||
new PBSFrontendPhaseService().compile(
|
||||
ctx,
|
||||
diagnostics,
|
||||
LogAggregator.empty(),
|
||||
BuildingIssueSink.empty());
|
||||
|
||||
assertTrue(diagnostics.stream().anyMatch(d ->
|
||||
d.getCode().equals(PbsSemanticsErrors.E_SEM_MISSING_PROJECT_FRAME.name())),
|
||||
diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -720,6 +774,7 @@ class PBSFrontendPhaseServiceTest {
|
||||
Files.writeString(sourceFile, """
|
||||
import { Gfx } from @sdk:gfx;
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
Gfx.clear_565(6577);
|
||||
@ -769,12 +824,22 @@ class PBSFrontendPhaseServiceTest {
|
||||
Files.writeString(sourceFile, """
|
||||
import { Gfx } from @sdk:gfx;
|
||||
|
||||
fn frame() -> int
|
||||
fn render() -> int
|
||||
{
|
||||
return Gfx.set_sprite(2, 0, 12, 18, 7, 3, true, false, true, 1);
|
||||
}
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
render();
|
||||
return;
|
||||
}
|
||||
""");
|
||||
Files.writeString(modBarrel, """
|
||||
pub fn render() -> int;
|
||||
pub fn frame() -> void;
|
||||
""");
|
||||
Files.writeString(modBarrel, "pub fn frame() -> int;");
|
||||
|
||||
final var projectTable = new ProjectTable();
|
||||
final var fileTable = new FileTable(1);
|
||||
@ -1099,6 +1164,7 @@ class PBSFrontendPhaseServiceTest {
|
||||
import { Log } from @sdk:log;
|
||||
import { Input } from @sdk:input;
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
if (Input.pad().a().pressed())
|
||||
@ -1182,6 +1248,7 @@ class PBSFrontendPhaseServiceTest {
|
||||
Files.writeString(sourceFile, """
|
||||
import { Input } from @sdk:input;
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
Input.pad().x().pressed();
|
||||
@ -1246,6 +1313,7 @@ class PBSFrontendPhaseServiceTest {
|
||||
Files.writeString(sourceFile, """
|
||||
import { Input } from @sdk:input;
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
let touch = Input.touch();
|
||||
|
||||
@ -4,6 +4,7 @@ import { Log } from @sdk:log;
|
||||
import { Input } from @sdk:input;
|
||||
import { Gfx } from @sdk:gfx;
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void
|
||||
{
|
||||
let touch = Input.touch();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user