sdk executable service bodies

This commit is contained in:
bQUARKz 2026-04-03 09:14:14 +01:00
parent ffd5f6c801
commit 0d01c0a522
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
11 changed files with 633 additions and 346 deletions

View File

@ -22,11 +22,13 @@ final class PbsExecutableCallableRegistryFactory {
final PbsAst.File ast, final PbsAst.File ast,
final ModuleId moduleId, final ModuleId moduleId,
final NameTable nameTable, final NameTable nameTable,
final Map<String, String> builtinCanonicalBySourceType,
final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) { final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) {
final var callableIdTable = new CallableTable(); final var callableIdTable = new CallableTable();
final var callableIdsByNameAndArity = new HashMap<PbsCallableResolutionKey, List<CallableId>>(); final var callableIdsByNameAndArity = new HashMap<PbsCallableResolutionKey, List<CallableId>>();
final var callableIdByDeclaration = new HashMap<PbsLowerableCallable, CallableId>(); final var callableIdByDeclaration = new HashMap<PbsLowerableCallable, CallableId>();
final var returnSlotsByCallableId = new HashMap<CallableId, Integer>(); final var returnSlotsByCallableId = new HashMap<CallableId, Integer>();
final var returnOwnerByCallableId = new HashMap<CallableId, String>();
final var tupleProjectionByCallableId = new HashMap<CallableId, PbsTupleProjection>(); final var tupleProjectionByCallableId = new HashMap<CallableId, PbsTupleProjection>();
final var typeSurfaceTable = new TypeSurfaceTable(); final var typeSurfaceTable = new TypeSurfaceTable();
final var callableShapeTable = new CallableShapeTable(); final var callableShapeTable = new CallableShapeTable();
@ -39,17 +41,21 @@ final class PbsExecutableCallableRegistryFactory {
callableIdsByNameAndArity, callableIdsByNameAndArity,
callableIdByDeclaration, callableIdByDeclaration,
returnSlotsByCallableId, returnSlotsByCallableId,
returnOwnerByCallableId,
tupleProjectionByCallableId, tupleProjectionByCallableId,
typeSurfaceTable, typeSurfaceTable,
callableShapeTable, callableShapeTable,
localCallables); localCallables,
builtinCanonicalBySourceType);
registerImportedCallables( registerImportedCallables(
nameTable, nameTable,
callableIdTable, callableIdTable,
callableIdsByNameAndArity, callableIdsByNameAndArity,
returnSlotsByCallableId, returnSlotsByCallableId,
tupleProjectionByCallableId, tupleProjectionByCallableId,
importedCallables); returnOwnerByCallableId,
importedCallables,
builtinCanonicalBySourceType);
final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>(); final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>();
final var callableSignatures = new ArrayList<CallableSignatureRef>(callableIdTable.size()); final var callableSignatures = new ArrayList<CallableSignatureRef>(callableIdTable.size());
@ -64,6 +70,7 @@ final class PbsExecutableCallableRegistryFactory {
callableIdsByNameAndArity, callableIdsByNameAndArity,
callableSignatureByCallableId, callableSignatureByCallableId,
returnSlotsByCallableId, returnSlotsByCallableId,
returnOwnerByCallableId,
tupleProjectionByCallableId, tupleProjectionByCallableId,
ReadOnlyList.wrap(callableSignatures)); ReadOnlyList.wrap(callableSignatures));
} }
@ -91,10 +98,12 @@ final class PbsExecutableCallableRegistryFactory {
final Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity, final Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
final Map<PbsLowerableCallable, CallableId> callableIdByDeclaration, final Map<PbsLowerableCallable, CallableId> callableIdByDeclaration,
final Map<CallableId, Integer> returnSlotsByCallableId, final Map<CallableId, Integer> returnSlotsByCallableId,
final Map<CallableId, String> returnOwnerByCallableId,
final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId, final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
final TypeSurfaceTable typeSurfaceTable, final TypeSurfaceTable typeSurfaceTable,
final CallableShapeTable callableShapeTable, final CallableShapeTable callableShapeTable,
final ReadOnlyList<PbsLowerableCallable> localCallables) { final ReadOnlyList<PbsLowerableCallable> localCallables,
final Map<String, String> builtinCanonicalBySourceType) {
for (final var declaredCallable : localCallables) { for (final var declaredCallable : localCallables) {
final var declaredFn = declaredCallable.functionDecl(); final var declaredFn = declaredCallable.functionDecl();
final var callableShapeId = callableShapeSurfaceService.callableShapeId( final var callableShapeId = callableShapeSurfaceService.callableShapeId(
@ -113,6 +122,13 @@ final class PbsExecutableCallableRegistryFactory {
ignored -> new ArrayList<>()) ignored -> new ArrayList<>())
.add(callableId); .add(callableId);
returnSlotsByCallableId.put(callableId, returnSlotsFor(declaredFn)); returnSlotsByCallableId.put(callableId, returnSlotsFor(declaredFn));
final var returnOwner = inferCallableReturnOwner(
declaredFn.returnKind(),
declaredFn.returnType(),
builtinCanonicalBySourceType);
if (!returnOwner.isBlank()) {
returnOwnerByCallableId.put(callableId, returnOwner);
}
final var tupleProjection = tupleProjectionOf(declaredFn.returnKind(), declaredFn.returnType()); final var tupleProjection = tupleProjectionOf(declaredFn.returnKind(), declaredFn.returnType());
if (tupleProjection != null) { if (tupleProjection != null) {
tupleProjectionByCallableId.put(callableId, tupleProjection); tupleProjectionByCallableId.put(callableId, tupleProjection);
@ -126,7 +142,9 @@ final class PbsExecutableCallableRegistryFactory {
final Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity, final Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
final Map<CallableId, Integer> returnSlotsByCallableId, final Map<CallableId, Integer> returnSlotsByCallableId,
final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId, final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) { final Map<CallableId, String> returnOwnerByCallableId,
final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables,
final Map<String, String> builtinCanonicalBySourceType) {
for (final var importedCallable : importedCallables) { for (final var importedCallable : importedCallables) {
final var callableId = callableIdTable.register( final var callableId = callableIdTable.register(
importedCallable.moduleId(), importedCallable.moduleId(),
@ -141,6 +159,13 @@ final class PbsExecutableCallableRegistryFactory {
ignored -> new ArrayList<>()) ignored -> new ArrayList<>())
.add(callableId); .add(callableId);
returnSlotsByCallableId.put(callableId, importedCallable.returnSlots()); returnSlotsByCallableId.put(callableId, importedCallable.returnSlots());
final var returnOwner = inferCallableReturnOwner(
importedCallable.returnKind(),
importedCallable.returnType(),
builtinCanonicalBySourceType);
if (!returnOwner.isBlank()) {
returnOwnerByCallableId.put(callableId, returnOwner);
}
final var tupleProjection = tupleProjectionOf(importedCallable.returnKind(), importedCallable.returnType()); final var tupleProjection = tupleProjectionOf(importedCallable.returnKind(), importedCallable.returnType());
if (tupleProjection != null) { if (tupleProjection != null) {
tupleProjectionByCallableId.put(callableId, tupleProjection); tupleProjectionByCallableId.put(callableId, tupleProjection);
@ -148,6 +173,20 @@ final class PbsExecutableCallableRegistryFactory {
} }
} }
private String inferCallableReturnOwner(
final PbsAst.ReturnKind returnKind,
final PbsAst.TypeRef returnType,
final Map<String, String> builtinCanonicalBySourceType) {
if (returnKind != PbsAst.ReturnKind.PLAIN || returnType == null || builtinCanonicalBySourceType == null) {
return "";
}
final var unwrapped = unwrapGroup(returnType);
if (unwrapped == null || unwrapped.kind() != PbsAst.TypeRefKind.SIMPLE) {
return "";
}
return builtinCanonicalBySourceType.getOrDefault(unwrapped.name(), "");
}
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType()); return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
} }

View File

@ -337,18 +337,21 @@ final class PbsExecutableCallsiteEmitter {
return ""; return "";
} }
final var receiverOwner = resolveReceiverOwnerForCallee(callExpr.callee(), context); final var receiverOwner = resolveReceiverOwnerForCallee(callExpr.callee(), context);
if (receiverOwner.isBlank()) { if (!receiverOwner.isBlank()) {
final var key = new PbsIntrinsicOwnerMethodKey(receiverOwner, calleeIdentity.memberSourceMethodName());
final var intrinsicCandidates = context.intrinsicByOwnerAndMethod().getOrDefault(key, List.of());
if (intrinsicCandidates.size() == 1) {
final var intrinsic = intrinsicCandidates.getFirst();
return context.intrinsicReturnOwnerByCanonical().getOrDefault(
new PbsIntrinsicCanonicalKey(intrinsic.canonicalName(), intrinsic.canonicalVersion()),
"");
}
}
final var callableCandidates = resolveCallableCandidates(callExpr, calleeIdentity, context);
if (callableCandidates.size() != 1) {
return ""; return "";
} }
final var key = new PbsIntrinsicOwnerMethodKey(receiverOwner, calleeIdentity.memberSourceMethodName()); return context.returnOwnerByCallableId().getOrDefault(callableCandidates.getFirst(), "");
final var intrinsicCandidates = context.intrinsicByOwnerAndMethod().getOrDefault(key, List.of());
if (intrinsicCandidates.size() != 1) {
return "";
}
final var intrinsic = intrinsicCandidates.getFirst();
return context.intrinsicReturnOwnerByCanonical().getOrDefault(
new PbsIntrinsicCanonicalKey(intrinsic.canonicalName(), intrinsic.canonicalVersion()),
"");
} }
private PbsCalleeIdentity resolveCalleeIdentity( private PbsCalleeIdentity resolveCalleeIdentity(

View File

@ -96,6 +96,10 @@ final class PbsExecutableLoweringContext {
return callableRegistry.returnSlotsByCallableId(); return callableRegistry.returnSlotsByCallableId();
} }
Map<p.studio.compiler.source.identifiers.CallableId, String> returnOwnerByCallableId() {
return callableRegistry.returnOwnerByCallableId();
}
Map<p.studio.compiler.source.identifiers.CallableId, PbsTupleProjection> tupleProjectionByCallableId() { Map<p.studio.compiler.source.identifiers.CallableId, PbsTupleProjection> tupleProjectionByCallableId() {
return callableRegistry.tupleProjectionByCallableId(); return callableRegistry.tupleProjectionByCallableId();
} }

View File

@ -58,6 +58,7 @@ public class PbsExecutableLoweringModels {
Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity, Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
Map<CallableId, CallableSignatureRef> callableSignatureByCallableId, Map<CallableId, CallableSignatureRef> callableSignatureByCallableId,
Map<CallableId, Integer> returnSlotsByCallableId, Map<CallableId, Integer> returnSlotsByCallableId,
Map<CallableId, String> returnOwnerByCallableId,
Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId, Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
ReadOnlyList<CallableSignatureRef> callableSignatures) { ReadOnlyList<CallableSignatureRef> callableSignatures) {
} }

View File

@ -45,6 +45,7 @@ public final class PbsExecutableLoweringService {
ast, ast,
normalizedModuleId, normalizedModuleId,
nameTable, nameTable,
metadataIndex.builtinCanonicalBySourceType(),
importedCallables); importedCallables);
final var intrinsicIdTable = new IntrinsicTable(); final var intrinsicIdTable = new IntrinsicTable();
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(callableRegistry.localCallables().size()); final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(callableRegistry.localCallables().size());

View File

@ -97,7 +97,7 @@ public final class PbsDeclarationSemanticsValidator {
binder.registerType(serviceDecl.name(), serviceDecl.span(), "service"); binder.registerType(serviceDecl.name(), serviceDecl.span(), "service");
binder.registerValue(serviceDecl.name(), serviceDecl.span(), "service singleton"); binder.registerValue(serviceDecl.name(), serviceDecl.span(), "service singleton");
binder.registerVisibleTopLevelSurface(serviceDecl.name(), serviceDecl.span(), "service"); binder.registerVisibleTopLevelSurface(serviceDecl.name(), serviceDecl.span(), "service");
validateServiceDeclaration(serviceDecl, binder, rules); validateServiceDeclaration(serviceDecl, binder, rules, interfaceModule, diagnostics);
continue; continue;
} }
@ -221,7 +221,9 @@ public final class PbsDeclarationSemanticsValidator {
private void validateServiceDeclaration( private void validateServiceDeclaration(
final PbsAst.ServiceDecl serviceDecl, final PbsAst.ServiceDecl serviceDecl,
final PbsNamespaceBinder binder, final PbsNamespaceBinder binder,
final PbsDeclarationRuleValidator rules) { final PbsDeclarationRuleValidator rules,
final boolean interfaceModule,
final DiagnosticSink diagnostics) {
final var methodScope = PbsCallableScope.serviceMethods(nameTable.register(serviceDecl.name())); final var methodScope = PbsCallableScope.serviceMethods(nameTable.register(serviceDecl.name()));
for (final var method : serviceDecl.methods()) { for (final var method : serviceDecl.methods()) {
validateCallable( validateCallable(
@ -235,9 +237,144 @@ public final class PbsDeclarationSemanticsValidator {
true, true,
binder, binder,
rules); rules);
if (interfaceModule) {
validateSdkExecutableServiceMethodBody(method, diagnostics);
}
} }
} }
private void validateSdkExecutableServiceMethodBody(
final PbsAst.FunctionDecl method,
final DiagnosticSink diagnostics) {
validateSdkExecutableBlock(method.body(), diagnostics);
}
private void validateSdkExecutableBlock(
final PbsAst.Block block,
final DiagnosticSink diagnostics) {
if (block == null) {
return;
}
for (final var statement : block.statements()) {
validateSdkExecutableStatement(statement, diagnostics);
}
if (block.tailExpression() != null) {
reportUnsupportedSdkExecutableConstruct(
block.tailExpression().span(),
"SDK executable service bodies do not support block tail expressions in the initial restricted subset",
diagnostics);
}
}
private void validateSdkExecutableStatement(
final PbsAst.Statement statement,
final DiagnosticSink diagnostics) {
switch (statement) {
case PbsAst.LetStatement letStatement ->
validateSdkExecutableExpression(letStatement.initializer(), diagnostics);
case PbsAst.AssignStatement assignStatement ->
validateSdkExecutableExpression(assignStatement.value(), diagnostics);
case PbsAst.ReturnStatement returnStatement ->
validateSdkExecutableExpression(returnStatement.value(), diagnostics);
case PbsAst.ExpressionStatement expressionStatement ->
validateSdkExecutableExpression(expressionStatement.expression(), diagnostics);
case PbsAst.IfStatement ignored ->
reportUnsupportedSdkExecutableConstruct(
statement.span(),
"SDK executable service bodies do not support if statements in the initial restricted subset",
diagnostics);
case PbsAst.ForStatement ignored ->
reportUnsupportedSdkExecutableConstruct(
statement.span(),
"SDK executable service bodies do not support for statements in the initial restricted subset",
diagnostics);
case PbsAst.WhileStatement ignored ->
reportUnsupportedSdkExecutableConstruct(
statement.span(),
"SDK executable service bodies do not support while statements in the initial restricted subset",
diagnostics);
case PbsAst.BreakStatement ignored ->
reportUnsupportedSdkExecutableConstruct(
statement.span(),
"SDK executable service bodies do not support break statements in the initial restricted subset",
diagnostics);
case PbsAst.ContinueStatement ignored ->
reportUnsupportedSdkExecutableConstruct(
statement.span(),
"SDK executable service bodies do not support continue statements in the initial restricted subset",
diagnostics);
}
}
private void validateSdkExecutableExpression(
final PbsAst.Expression expression,
final DiagnosticSink diagnostics) {
if (expression == null) {
return;
}
switch (expression) {
case PbsAst.IdentifierExpr ignored -> {
}
case PbsAst.IntLiteralExpr ignored -> {
}
case PbsAst.FloatLiteralExpr ignored -> {
}
case PbsAst.BoundedLiteralExpr ignored -> {
}
case PbsAst.StringLiteralExpr ignored -> {
}
case PbsAst.BoolLiteralExpr ignored -> {
}
case PbsAst.ThisExpr ignored -> {
}
case PbsAst.UnitExpr ignored -> {
}
case PbsAst.NoneExpr ignored -> {
}
case PbsAst.UnaryExpr unaryExpr ->
validateSdkExecutableExpression(unaryExpr.expression(), diagnostics);
case PbsAst.BinaryExpr binaryExpr -> {
validateSdkExecutableExpression(binaryExpr.left(), diagnostics);
validateSdkExecutableExpression(binaryExpr.right(), diagnostics);
}
case PbsAst.CallExpr callExpr -> {
validateSdkExecutableExpression(callExpr.callee(), diagnostics);
for (final var argument : callExpr.arguments()) {
validateSdkExecutableExpression(argument, diagnostics);
}
}
case PbsAst.MemberExpr memberExpr ->
validateSdkExecutableExpression(memberExpr.receiver(), diagnostics);
case PbsAst.GroupExpr groupExpr ->
validateSdkExecutableExpression(groupExpr.expression(), diagnostics);
case PbsAst.NewExpr newExpr -> {
for (final var argument : newExpr.arguments()) {
validateSdkExecutableExpression(argument, diagnostics);
}
}
case PbsAst.TupleExpr tupleExpr -> {
for (final var item : tupleExpr.items()) {
validateSdkExecutableExpression(item.expression(), diagnostics);
}
}
default -> reportUnsupportedSdkExecutableConstruct(
expression.span(),
"SDK executable service bodies use an unsupported expression form in the initial restricted subset",
diagnostics);
}
}
private void reportUnsupportedSdkExecutableConstruct(
final Span span,
final String message,
final DiagnosticSink diagnostics) {
p.studio.compiler.source.diagnostics.Diagnostics.error(
diagnostics,
PbsSemanticsErrors.E_SEM_SDK_EXEC_SERVICE_UNSUPPORTED_CONSTRUCT.name(),
message,
span);
}
private void validateContractDeclaration( private void validateContractDeclaration(
final PbsAst.ContractDecl contractDecl, final PbsAst.ContractDecl contractDecl,
final PbsNamespaceBinder binder, final PbsNamespaceBinder binder,

View File

@ -68,6 +68,7 @@ public enum PbsSemanticsErrors {
E_SEM_HANDLE_NON_RESULT, E_SEM_HANDLE_NON_RESULT,
E_SEM_HANDLE_ERROR_MISMATCH, E_SEM_HANDLE_ERROR_MISMATCH,
E_SEM_HANDLE_ARM_TERMINAL_INVALID, E_SEM_HANDLE_ARM_TERMINAL_INVALID,
E_SEM_SDK_EXEC_SERVICE_UNSUPPORTED_CONSTRUCT,
E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE, E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE,
W_SEM_IGNORED_VALUE, W_SEM_IGNORED_VALUE,
} }

View File

@ -104,7 +104,9 @@ class PbsGateUSdkInterfaceConformanceTest {
assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream() assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream()
.anyMatch(t -> t.sourceTypeName().equals("Color"))); .anyMatch(t -> t.sourceTypeName().equals("Color")));
assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream() assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream()
.anyMatch(t -> t.sourceTypeName().equals("Input") && t.canonicalTypeName().equals("input"))); .anyMatch(t -> t.sourceTypeName().equals("InputType") && t.canonicalTypeName().equals("input")));
assertTrue(positive.irBackend().getReservedMetadata().builtinConstSurfaces().stream()
.anyMatch(c -> c.sourceConstName().equals("LowInput") && c.canonicalTarget().equals("input")));
assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream() assertTrue(positive.irBackend().getReservedMetadata().builtinTypeSurfaces().stream()
.anyMatch(t -> t.sourceTypeName().equals("InputButton") .anyMatch(t -> t.sourceTypeName().equals("InputButton")
&& t.intrinsics().stream().anyMatch(i -> i.canonicalName().equals("input.button.hold")))); && t.intrinsics().stream().anyMatch(i -> i.canonicalName().equals("input.button.hold"))));
@ -157,6 +159,47 @@ class PbsGateUSdkInterfaceConformanceTest {
assertStableDiagnosticIdentity(notPublic, PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name(), DiagnosticPhase.LINKING); assertStableDiagnosticIdentity(notPublic, PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name(), DiagnosticPhase.LINKING);
} }
@Test
void gateU_shouldRejectUnsupportedControlFlowInExecutableSdkServiceBodies() {
final var diagnostics = DiagnosticSink.empty();
new PbsFrontendCompiler().compileFile(
new FileId(60),
"""
[BuiltinType(name = "input", version = 1)]
declare builtin type InputType() {
[IntrinsicCall(name = "touch", version = 1)]
fn touch() -> InputTouch;
}
[BuiltinType(name = "input.touch", version = 1)]
declare builtin type InputTouch() {
[IntrinsicCall(name = "x", version = 1)]
fn x() -> int;
}
[BuiltinConst(target = "input", name = "global", version = 1)]
declare const LowInput: InputType;
declare service Input {
fn touch() -> InputTouch {
if (true) {
return LowInput.touch();
}
return LowInput.touch();
}
}
""",
diagnostics,
SourceKind.SDK_INTERFACE);
final var diagnostic = firstDiagnostic(diagnostics,
d -> d.getCode().equals(PbsSemanticsErrors.E_SEM_SDK_EXEC_SERVICE_UNSUPPORTED_CONSTRUCT.name()));
assertStableDiagnosticIdentity(
diagnostic,
PbsSemanticsErrors.E_SEM_SDK_EXEC_SERVICE_UNSUPPORTED_CONSTRUCT.name(),
DiagnosticPhase.STATIC_SEMANTICS);
}
@Test @Test
void gateU_shouldParseReservedDeclarationsInInterfaceModeOnly() { void gateU_shouldParseReservedDeclarationsInInterfaceModeOnly() {
final var source = """ final var source = """

View File

@ -1410,13 +1410,28 @@ class PBSFrontendPhaseServiceTest {
+ " executableNames=" + " executableNames="
+ executableNames); + executableNames);
final var frameExecutable = frameExecutableOptional.orElseThrow(); final var frameExecutable = frameExecutableOptional.orElseThrow();
final var callableCalls = frameExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC)
.map(instruction -> instruction.calleeCallableName())
.toList();
final var intrinsicCalls = frameExecutable.instructions().stream() final var intrinsicCalls = frameExecutable.instructions().stream()
.filter(instruction -> .filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC) instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC)
.toList(); .toList();
final var inputPadExecutable = irBackend.getExecutableFunctions().stream()
.filter(function -> "Input.pad".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var inputPadIntrinsics = inputPadExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName())
.toList();
assertTrue(callableCalls.contains("Input.pad"));
assertTrue(intrinsicCalls.stream().anyMatch(instruction -> Integer.valueOf(1).equals(instruction.expectedArgSlots()))); assertTrue(intrinsicCalls.stream().anyMatch(instruction -> Integer.valueOf(1).equals(instruction.expectedArgSlots())));
assertTrue(intrinsicCalls.stream().anyMatch(instruction -> assertTrue(inputPadIntrinsics.contains("input.pad"));
instruction.intrinsicCall() != null && "input.pad".equals(instruction.intrinsicCall().canonicalName())));
assertTrue(intrinsicCalls.stream().anyMatch(instruction -> assertTrue(intrinsicCalls.stream().anyMatch(instruction ->
instruction.intrinsicCall() != null && "input.pad.a".equals(instruction.intrinsicCall().canonicalName()))); instruction.intrinsicCall() != null && "input.pad.a".equals(instruction.intrinsicCall().canonicalName())));
assertTrue(intrinsicCalls.stream().anyMatch(instruction -> assertTrue(intrinsicCalls.stream().anyMatch(instruction ->
@ -1481,16 +1496,43 @@ class PBSFrontendPhaseServiceTest {
.filter(function -> "frame".equals(function.callableName())) .filter(function -> "frame".equals(function.callableName()))
.findFirst() .findFirst()
.orElseThrow(); .orElseThrow();
final var callableCalls = frameExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC)
.map(instruction -> instruction.calleeCallableName())
.toList();
final var intrinsicCalls = frameExecutable.instructions().stream() final var intrinsicCalls = frameExecutable.instructions().stream()
.filter(instruction -> .filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null) && instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName()) .map(instruction -> instruction.intrinsicCall().canonicalName())
.toList(); .toList();
assertTrue(intrinsicCalls.contains("input.pad")); final var inputPadExecutable = irBackend.getExecutableFunctions().stream()
.filter(function -> "Input.pad".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var inputTouchExecutable = irBackend.getExecutableFunctions().stream()
.filter(function -> "Input.touch".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var inputPadIntrinsics = inputPadExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName())
.toList();
final var inputTouchIntrinsics = inputTouchExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName())
.toList();
assertTrue(callableCalls.contains("Input.pad"));
assertTrue(callableCalls.contains("Input.touch"));
assertTrue(inputPadIntrinsics.contains("input.pad"));
assertTrue(intrinsicCalls.contains("input.pad.x")); assertTrue(intrinsicCalls.contains("input.pad.x"));
assertTrue(intrinsicCalls.contains("input.button.pressed")); assertTrue(intrinsicCalls.contains("input.button.pressed"));
assertTrue(intrinsicCalls.contains("input.touch")); assertTrue(inputTouchIntrinsics.contains("input.touch"));
assertTrue(intrinsicCalls.contains("input.touch.x")); assertTrue(intrinsicCalls.contains("input.touch.x"));
} }
@ -1547,13 +1589,29 @@ class PBSFrontendPhaseServiceTest {
.filter(function -> "frame".equals(function.callableName())) .filter(function -> "frame".equals(function.callableName()))
.findFirst() .findFirst()
.orElseThrow(); .orElseThrow();
final var callableCalls = frameExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC)
.map(instruction -> instruction.calleeCallableName())
.toList();
final var intrinsicCalls = frameExecutable.instructions().stream() final var intrinsicCalls = frameExecutable.instructions().stream()
.filter(instruction -> .filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null) && instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName()) .map(instruction -> instruction.intrinsicCall().canonicalName())
.toList(); .toList();
assertTrue(intrinsicCalls.contains("input.touch")); final var inputTouchExecutable = irBackend.getExecutableFunctions().stream()
.filter(function -> "Input.touch".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var inputTouchIntrinsics = inputTouchExecutable.instructions().stream()
.filter(instruction ->
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
&& instruction.intrinsicCall() != null)
.map(instruction -> instruction.intrinsicCall().canonicalName())
.toList();
assertTrue(callableCalls.contains("Input.touch"));
assertTrue(inputTouchIntrinsics.contains("input.touch"));
assertTrue(intrinsicCalls.contains("input.touch.x")); assertTrue(intrinsicCalls.contains("input.touch.x"));
assertTrue(intrinsicCalls.contains("input.touch.y")); assertTrue(intrinsicCalls.contains("input.touch.y"));
} }

File diff suppressed because it is too large Load Diff