a little bit of gfx
This commit is contained in:
parent
079db44cd1
commit
59e33d6639
@ -79,6 +79,10 @@ public final class PbsReservedMetadataExtractor {
|
||||
abiModule,
|
||||
abiMethod,
|
||||
abiVersion,
|
||||
switch (signature.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
},
|
||||
capabilityAttribute.isPresent(),
|
||||
capability,
|
||||
signature.span()));
|
||||
|
||||
@ -28,6 +28,7 @@ final class PbsExecutableBodyLowerer {
|
||||
final PbsExecutableCallableRegistry callableRegistry,
|
||||
final IntrinsicTable intrinsicIdTable) {
|
||||
final var localSlotByNameId = initialLocalSlots(functionDecl, nameTable);
|
||||
final var localOwnerByNameId = initialLocalOwners(functionDecl, nameTable, metadataIndex);
|
||||
final var context = new PbsExecutableLoweringContext(
|
||||
diagnostics,
|
||||
nameTable,
|
||||
@ -35,6 +36,7 @@ final class PbsExecutableBodyLowerer {
|
||||
callableRegistry,
|
||||
intrinsicIdTable,
|
||||
localSlotByNameId,
|
||||
localOwnerByNameId,
|
||||
functionDecl.parameters().size());
|
||||
final var terminated = lowerBlock(functionDecl.body(), context);
|
||||
finalizeFunctionInstructions(functionDecl, context, terminated);
|
||||
@ -70,6 +72,20 @@ final class PbsExecutableBodyLowerer {
|
||||
return localSlotByNameId;
|
||||
}
|
||||
|
||||
private Map<NameId, String> initialLocalOwners(
|
||||
final PbsAst.FunctionDecl functionDecl,
|
||||
final NameTable nameTable,
|
||||
final PbsExecutableMetadataIndex metadataIndex) {
|
||||
final var localOwnerByNameId = new HashMap<NameId, String>();
|
||||
for (final var parameter : functionDecl.parameters()) {
|
||||
final var ownerCanonical = resolveBuiltinOwnerCanonical(parameter.typeRef(), metadataIndex);
|
||||
if (!ownerCanonical.isBlank()) {
|
||||
localOwnerByNameId.put(nameTable.register(parameter.name()), ownerCanonical);
|
||||
}
|
||||
}
|
||||
return localOwnerByNameId;
|
||||
}
|
||||
|
||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||
return switch (functionDecl.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
@ -137,8 +153,10 @@ final class PbsExecutableBodyLowerer {
|
||||
private boolean lowerLetStatement(
|
||||
final PbsAst.LetStatement letStatement,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(letStatement.initializer(), context);
|
||||
lowerExpression(letStatement.initializer(), context);
|
||||
final var localSlot = context.declareLocalSlot(letStatement.name());
|
||||
context.bindLocalOwner(letStatement.name(), localOwner);
|
||||
emitSetLocal(localSlot, letStatement.span(), context);
|
||||
return false;
|
||||
}
|
||||
@ -161,17 +179,40 @@ final class PbsExecutableBodyLowerer {
|
||||
return false;
|
||||
}
|
||||
if (assignStatement.operator() == PbsAst.AssignOperator.ASSIGN) {
|
||||
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(assignStatement.value(), context);
|
||||
lowerExpression(assignStatement.value(), context);
|
||||
context.bindLocalOwner(target.rootName(), localOwner);
|
||||
emitSetLocal(targetSlot, assignStatement.span(), context);
|
||||
return false;
|
||||
}
|
||||
emitGetLocal(targetSlot, assignStatement.span(), context);
|
||||
lowerExpression(assignStatement.value(), context);
|
||||
emitBinaryOperatorInstruction(compoundAssignBinaryOperator(assignStatement.operator()), assignStatement.span(), context);
|
||||
context.bindLocalOwner(target.rootName(), "");
|
||||
emitSetLocal(targetSlot, assignStatement.span(), context);
|
||||
return false;
|
||||
}
|
||||
|
||||
private String resolveBuiltinOwnerCanonical(
|
||||
final PbsAst.TypeRef typeRef,
|
||||
final PbsExecutableMetadataIndex metadataIndex) {
|
||||
final var unwrapped = unwrapGroup(typeRef);
|
||||
if (unwrapped == null || unwrapped.kind() != PbsAst.TypeRefKind.SIMPLE) {
|
||||
return "";
|
||||
}
|
||||
return metadataIndex.builtinCanonicalBySourceType().getOrDefault(unwrapped.name(), "");
|
||||
}
|
||||
|
||||
private PbsAst.TypeRef unwrapGroup(final PbsAst.TypeRef typeRef) {
|
||||
if (typeRef == null) {
|
||||
return null;
|
||||
}
|
||||
if (typeRef.kind() != PbsAst.TypeRefKind.GROUP) {
|
||||
return typeRef;
|
||||
}
|
||||
return unwrapGroup(typeRef.inner());
|
||||
}
|
||||
|
||||
private boolean lowerReturnStatement(
|
||||
final PbsAst.ReturnStatement returnStatement,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
|
||||
@ -43,10 +43,28 @@ final class PbsExecutableCallsiteEmitter {
|
||||
final PbsExecutableLoweringContext context) {
|
||||
return new PbsResolvedCallsiteCandidates(
|
||||
resolveCallableCandidates(callExpr, calleeIdentity, context),
|
||||
context.hostByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of()),
|
||||
resolveHostCandidates(callExpr, calleeIdentity, context),
|
||||
resolveIntrinsicCandidates(callExpr, calleeIdentity, context));
|
||||
}
|
||||
|
||||
private List<IRReservedMetadata.HostMethodBinding> resolveHostCandidates(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsCalleeIdentity calleeIdentity,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
final var hostCandidates = context.hostByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of());
|
||||
if (hostCandidates.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
final var ownerHint = hostOwnerHint(callExpr.callee());
|
||||
if (ownerHint.isBlank()) {
|
||||
return hostCandidates;
|
||||
}
|
||||
final var filtered = hostCandidates.stream()
|
||||
.filter(candidate -> ownerHint.equals(candidate.ownerName()))
|
||||
.toList();
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private List<CallableId> resolveCallableCandidates(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsCalleeIdentity calleeIdentity,
|
||||
@ -126,7 +144,7 @@ final class PbsExecutableCallsiteEmitter {
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsExecutableLoweringContext context,
|
||||
final IRReservedMetadata.HostMethodBinding host) {
|
||||
final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee());
|
||||
final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee(), context);
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
|
||||
"",
|
||||
@ -135,7 +153,7 @@ final class PbsExecutableCallsiteEmitter {
|
||||
host.abiMethod(),
|
||||
host.abiVersion(),
|
||||
effectiveArgSlots,
|
||||
0),
|
||||
host.retSlots()),
|
||||
null,
|
||||
callExpr.span()));
|
||||
}
|
||||
@ -144,7 +162,7 @@ final class PbsExecutableCallsiteEmitter {
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsExecutableLoweringContext context,
|
||||
final IRReservedMetadata.IntrinsicSurface intrinsic) {
|
||||
final var effectiveArgSlots = intrinsic.argSlots() + implicitReceiverArgSlots(callExpr.callee());
|
||||
final var effectiveArgSlots = intrinsic.argSlots() + implicitReceiverArgSlots(callExpr.callee(), context);
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
@ -176,7 +194,7 @@ final class PbsExecutableCallsiteEmitter {
|
||||
reportUnsupportedLowering("executable lowering resolved callable without signature metadata", callExpr.span(), context);
|
||||
return;
|
||||
}
|
||||
final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee());
|
||||
final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee(), context);
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||
calleeSignature.moduleId(),
|
||||
@ -211,6 +229,23 @@ final class PbsExecutableCallsiteEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
String resolveExpressionOwnerForLowering(
|
||||
final PbsAst.Expression expression,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
return resolveExpressionOwner(expression, context);
|
||||
}
|
||||
|
||||
private String hostOwnerHint(final PbsAst.Expression callee) {
|
||||
return switch (callee) {
|
||||
case PbsAst.MemberExpr memberExpr -> {
|
||||
final var rootName = memberRootName(memberExpr.receiver());
|
||||
yield rootName == null ? "" : rootName;
|
||||
}
|
||||
case PbsAst.GroupExpr groupExpr -> hostOwnerHint(groupExpr.expression());
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveExpressionOwner(
|
||||
final PbsAst.Expression expression,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
@ -219,7 +254,7 @@ final class PbsExecutableCallsiteEmitter {
|
||||
}
|
||||
return switch (expression) {
|
||||
case PbsAst.IdentifierExpr identifierExpr ->
|
||||
context.builtinConstOwnerByNameId().getOrDefault(context.nameTable().register(identifierExpr.name()), "");
|
||||
resolveIdentifierOwner(identifierExpr.name(), context);
|
||||
case PbsAst.CallExpr callExpr -> resolveCallReturnOwner(callExpr, context);
|
||||
case PbsAst.MemberExpr memberExpr -> resolveExpressionOwner(memberExpr.receiver(), context);
|
||||
case PbsAst.GroupExpr groupExpr -> resolveExpressionOwner(groupExpr.expression(), context);
|
||||
@ -227,6 +262,16 @@ final class PbsExecutableCallsiteEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveIdentifierOwner(
|
||||
final String identifier,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
final var localOwner = context.resolveLocalOwner(identifier);
|
||||
if (!localOwner.isBlank()) {
|
||||
return localOwner;
|
||||
}
|
||||
return context.builtinConstOwnerByNameId().getOrDefault(context.nameTable().register(identifier), "");
|
||||
}
|
||||
|
||||
private String resolveCallReturnOwner(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
@ -302,17 +347,22 @@ final class PbsExecutableCallsiteEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
private int implicitReceiverArgSlots(final PbsAst.Expression callee) {
|
||||
private int implicitReceiverArgSlots(
|
||||
final PbsAst.Expression callee,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
if (!(callee instanceof PbsAst.MemberExpr memberExpr)) {
|
||||
return 0;
|
||||
}
|
||||
return receiverProducesRuntimeValue(memberExpr.receiver()) ? 1 : 0;
|
||||
return receiverProducesRuntimeValue(memberExpr.receiver(), context) ? 1 : 0;
|
||||
}
|
||||
|
||||
private boolean receiverProducesRuntimeValue(final PbsAst.Expression receiver) {
|
||||
private boolean receiverProducesRuntimeValue(
|
||||
final PbsAst.Expression receiver,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
return switch (receiver) {
|
||||
case PbsAst.CallExpr ignored -> true;
|
||||
case PbsAst.GroupExpr groupExpr -> receiverProducesRuntimeValue(groupExpr.expression());
|
||||
case PbsAst.IdentifierExpr identifierExpr -> context.resolveLocalSlot(identifierExpr.name()) != null;
|
||||
case PbsAst.GroupExpr groupExpr -> receiverProducesRuntimeValue(groupExpr.expression(), context);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ final class PbsExecutableLoweringContext {
|
||||
private final PbsExecutableCallableRegistry callableRegistry;
|
||||
private final IntrinsicTable intrinsicIdTable;
|
||||
private final Map<NameId, Integer> localSlotByNameId;
|
||||
private final Map<NameId, String> localOwnerByNameId;
|
||||
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
||||
private final ArrayDeque<PbsLoopTargets> loopTargets = new ArrayDeque<>();
|
||||
private int nextLabelId = 0;
|
||||
@ -29,6 +30,7 @@ final class PbsExecutableLoweringContext {
|
||||
final PbsExecutableCallableRegistry callableRegistry,
|
||||
final IntrinsicTable intrinsicIdTable,
|
||||
final Map<NameId, Integer> localSlotByNameId,
|
||||
final Map<NameId, String> localOwnerByNameId,
|
||||
final int initialLocalSlot) {
|
||||
this.diagnostics = diagnostics;
|
||||
this.nameTable = nameTable;
|
||||
@ -36,6 +38,7 @@ final class PbsExecutableLoweringContext {
|
||||
this.callableRegistry = callableRegistry;
|
||||
this.intrinsicIdTable = intrinsicIdTable;
|
||||
this.localSlotByNameId = localSlotByNameId == null ? new HashMap<>() : localSlotByNameId;
|
||||
this.localOwnerByNameId = localOwnerByNameId == null ? new HashMap<>() : localOwnerByNameId;
|
||||
this.nextLocalSlot = Math.max(0, initialLocalSlot);
|
||||
}
|
||||
|
||||
@ -55,6 +58,10 @@ final class PbsExecutableLoweringContext {
|
||||
return metadataIndex.builtinConstOwnerByNameId();
|
||||
}
|
||||
|
||||
Map<String, String> builtinCanonicalBySourceType() {
|
||||
return metadataIndex.builtinCanonicalBySourceType();
|
||||
}
|
||||
|
||||
Map<PbsIntrinsicOwnerMethodKey, List<p.studio.compiler.models.IRReservedMetadata.IntrinsicSurface>> intrinsicByOwnerAndMethod() {
|
||||
return metadataIndex.intrinsicByOwnerAndMethod();
|
||||
}
|
||||
@ -83,6 +90,27 @@ final class PbsExecutableLoweringContext {
|
||||
return localSlotByNameId;
|
||||
}
|
||||
|
||||
String resolveLocalOwner(final String localName) {
|
||||
if (localName == null || localName.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
return localOwnerByNameId.getOrDefault(nameTable.register(localName), "");
|
||||
}
|
||||
|
||||
void bindLocalOwner(
|
||||
final String localName,
|
||||
final String ownerCanonicalName) {
|
||||
if (localName == null || localName.isBlank()) {
|
||||
return;
|
||||
}
|
||||
final var nameId = nameTable.register(localName);
|
||||
if (ownerCanonicalName == null || ownerCanonicalName.isBlank()) {
|
||||
localOwnerByNameId.remove(nameId);
|
||||
return;
|
||||
}
|
||||
localOwnerByNameId.put(nameId, ownerCanonicalName);
|
||||
}
|
||||
|
||||
Integer resolveLocalSlot(final String localName) {
|
||||
if (localName == null || localName.isBlank()) {
|
||||
return null;
|
||||
|
||||
@ -42,6 +42,7 @@ public class PbsExecutableLoweringModels {
|
||||
|
||||
record PbsExecutableMetadataIndex(
|
||||
Map<NameId, List<IRReservedMetadata.HostMethodBinding>> hostByMethodName,
|
||||
Map<String, String> builtinCanonicalBySourceType,
|
||||
Map<NameId, String> builtinConstOwnerByNameId,
|
||||
Map<PbsIntrinsicOwnerMethodKey, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByOwnerAndMethod,
|
||||
Map<PbsIntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical) {
|
||||
|
||||
@ -42,6 +42,7 @@ final class PbsExecutableMetadataIndexFactory {
|
||||
diagnostics);
|
||||
return new PbsExecutableMetadataIndex(
|
||||
hostByMethodName,
|
||||
builtinCanonicalBySourceType,
|
||||
builtinConstOwnerByNameId,
|
||||
intrinsicByOwnerAndMethod,
|
||||
intrinsicReturnOwnerByCanonical);
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
[BuiltinType(name = "Color", version = 1)]
|
||||
declare builtin type Color(
|
||||
pub r: int,
|
||||
pub g: int,
|
||||
pub b: int,
|
||||
pub a: int
|
||||
pub raw: int
|
||||
) {
|
||||
[IntrinsicCall(name = "core.color.pack", version = 1)]
|
||||
fn pack() -> int;
|
||||
}
|
||||
|
||||
@ -1,7 +1,109 @@
|
||||
import { Color } from @core:color;
|
||||
|
||||
declare host Gfx {
|
||||
[Host(module = "gfx", name = "draw_pixel", version = 1)]
|
||||
declare host LowGfx {
|
||||
[Host(module = "gfx", name = "clear", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_pixel(x: int, y: int, color: Color) -> void;
|
||||
fn clear(color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "fill_rect", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "draw_line", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "draw_circle", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_circle(x: int, y: int, r: int, color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "draw_disc", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_disc(x: int, y: int, r: int, border_color: Color, fill_color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "draw_square", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "set_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn set_sprite(
|
||||
asset_name: str,
|
||||
index: int,
|
||||
x: int,
|
||||
y: int,
|
||||
tile_id: int,
|
||||
palette_id: int,
|
||||
active: bool,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
|
||||
[Host(module = "gfx", name = "draw_text", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "clear_565", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn clear_565(color_565: int) -> void;
|
||||
}
|
||||
|
||||
declare service Gfx
|
||||
{
|
||||
fn clear(color: Color) -> void
|
||||
{
|
||||
LowGfx.clear(color);
|
||||
}
|
||||
|
||||
fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.fill_rect(x, y, w, h, color);
|
||||
}
|
||||
|
||||
fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_line(x1, y1, x2, y2, color);
|
||||
}
|
||||
|
||||
fn draw_circle(x: int, y: int, r: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_circle(x, y, r, color);
|
||||
}
|
||||
|
||||
fn draw_disc(x: int, y: int, r: int, border_color: Color, fill_color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_disc(x, y, r, border_color, fill_color);
|
||||
}
|
||||
|
||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_square(x, y, w, h, border_color, fill_color);
|
||||
}
|
||||
|
||||
fn set_sprite(
|
||||
asset_name: str,
|
||||
index: int,
|
||||
x: int,
|
||||
y: int,
|
||||
tile_id: int,
|
||||
palette_id: int,
|
||||
active: bool,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int
|
||||
{
|
||||
return LowGfx.set_sprite(asset_name, index, x, y, tile_id, palette_id, active, flip_x, flip_y, priority);
|
||||
}
|
||||
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_text(x, y, message, color);
|
||||
}
|
||||
|
||||
fn clear_565(color_565: int) -> void
|
||||
{
|
||||
LowGfx.clear_565(color_565);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
pub host Gfx;
|
||||
mod host LowGfx;
|
||||
pub service Gfx;
|
||||
|
||||
@ -304,12 +304,8 @@ class PbsFrontendCompilerTest {
|
||||
final var source = """
|
||||
[BuiltinType(name = "Color", version = 1)]
|
||||
declare builtin type Color(
|
||||
pub r: int,
|
||||
pub g: int,
|
||||
pub b: int
|
||||
pub raw: int
|
||||
) {
|
||||
[IntrinsicCall(name = "core.color.pack")]
|
||||
fn pack() -> int;
|
||||
}
|
||||
declare host Gfx {
|
||||
[Host(module = "gfx", name = "draw_pixel", version = 1)]
|
||||
@ -332,7 +328,7 @@ class PbsFrontendCompilerTest {
|
||||
assertEquals(1, fileBackend.reservedMetadata().requiredCapabilities().size());
|
||||
assertEquals("gfx", fileBackend.reservedMetadata().requiredCapabilities().getFirst());
|
||||
assertEquals("Color", fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().canonicalTypeName());
|
||||
assertEquals(3, fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().fields().size());
|
||||
assertEquals(1, fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().fields().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -453,10 +449,7 @@ class PbsFrontendCompilerTest {
|
||||
final var source = """
|
||||
[BuiltinType(name = "Color", version = 1)]
|
||||
declare builtin type Color(
|
||||
pub r: int,
|
||||
pub g: int,
|
||||
pub b: int,
|
||||
pub a: int
|
||||
pub raw: int
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@ -116,7 +116,10 @@ class PbsGateUSdkInterfaceConformanceTest {
|
||||
.anyMatch(t -> t.sourceTypeName().equals("InputButton")
|
||||
&& t.intrinsics().stream().anyMatch(i -> i.canonicalName().equals("input.button.hold"))));
|
||||
assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream()
|
||||
.anyMatch(h -> h.ownerName().equals("Gfx")));
|
||||
.anyMatch(h -> h.ownerName().equals("LowGfx")
|
||||
&& h.abiModule().equals("gfx")
|
||||
&& h.abiMethod().equals("clear")
|
||||
&& h.abiVersion() == 1));
|
||||
assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream()
|
||||
.anyMatch(h -> h.ownerName().equals("LowLog")
|
||||
&& h.abiModule().equals("log")
|
||||
|
||||
@ -525,7 +525,7 @@ class PBSFrontendPhaseServiceTest {
|
||||
d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name())));
|
||||
assertEquals(0, irBackend.getFunctions().size());
|
||||
assertTrue(irBackend.getReservedMetadata().builtinTypeSurfaces().stream()
|
||||
.anyMatch(t -> t.sourceTypeName().equals("Color") && t.fields().size() == 4));
|
||||
.anyMatch(t -> t.sourceTypeName().equals("Color") && t.fields().size() == 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -578,7 +578,56 @@ class PBSFrontendPhaseServiceTest {
|
||||
d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name())));
|
||||
assertEquals(0, irBackend.getFunctions().size());
|
||||
assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream()
|
||||
.anyMatch(h -> h.ownerName().equals("Gfx") && h.sourceMethodName().equals("draw_pixel")));
|
||||
.anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("clear")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLowerSdkGfxServiceFacadeCallsWithoutHostCallableAmbiguity() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-facade-call");
|
||||
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 { Gfx } from @sdk:gfx;
|
||||
|
||||
fn frame() -> void
|
||||
{
|
||||
Gfx.clear_565(6577);
|
||||
}
|
||||
""");
|
||||
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("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())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -1008,6 +1057,69 @@ class PBSFrontendPhaseServiceTest {
|
||||
assertTrue(intrinsicCalls.contains("input.touch.x"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveIntrinsicCallsOnBuiltinValuesStoredInLocals() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-intrinsic-local-builtin-receiver");
|
||||
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, """
|
||||
import { Input } from @sdk:input;
|
||||
|
||||
fn frame() -> void
|
||||
{
|
||||
let touch = Input.touch();
|
||||
touch.x();
|
||||
touch.y();
|
||||
}
|
||||
""");
|
||||
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))),
|
||||
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())),
|
||||
diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
final var frameExecutable = irBackend.getExecutableFunctions().stream()
|
||||
.filter(function -> "frame".equals(function.callableName()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
final var intrinsicCalls = frameExecutable.instructions().stream()
|
||||
.filter(instruction ->
|
||||
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
|
||||
&& instruction.intrinsicCall() != null)
|
||||
.map(instruction -> instruction.intrinsicCall().canonicalName())
|
||||
.toList();
|
||||
assertTrue(intrinsicCalls.contains("input.touch"));
|
||||
assertTrue(intrinsicCalls.contains("input.touch.x"));
|
||||
assertTrue(intrinsicCalls.contains("input.touch.y"));
|
||||
}
|
||||
|
||||
private void registerFile(
|
||||
final ProjectId projectId,
|
||||
final Path projectRoot,
|
||||
|
||||
@ -32,6 +32,7 @@ public record IRReservedMetadata(
|
||||
String abiModule,
|
||||
String abiMethod,
|
||||
long abiVersion,
|
||||
int retSlots,
|
||||
boolean capabilityDeclared,
|
||||
String requiredCapability,
|
||||
Span span) {
|
||||
@ -40,6 +41,9 @@ public record IRReservedMetadata(
|
||||
sourceMethodName = Objects.requireNonNull(sourceMethodName, "sourceMethodName");
|
||||
abiModule = Objects.requireNonNull(abiModule, "abiModule");
|
||||
abiMethod = Objects.requireNonNull(abiMethod, "abiMethod");
|
||||
if (retSlots < 0) {
|
||||
throw new IllegalArgumentException("host retSlots must be non-negative");
|
||||
}
|
||||
requiredCapability = requiredCapability == null ? "" : requiredCapability;
|
||||
span = Objects.requireNonNull(span, "span");
|
||||
}
|
||||
|
||||
@ -6,5 +6,5 @@
|
||||
"app_version": "0.1.0",
|
||||
"app_mode": "Game",
|
||||
"entrypoint": "0",
|
||||
"capabilities": ["log"]
|
||||
"capabilities": ["log", "gfx"]
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -1,8 +1,16 @@
|
||||
import { Color } from @core:color;
|
||||
|
||||
import { Log } from @sdk:log;
|
||||
import { Input } from @sdk:input;
|
||||
import { Gfx } from @sdk:gfx;
|
||||
|
||||
fn frame() -> void
|
||||
{
|
||||
let touch = Input.touch();
|
||||
|
||||
Gfx.clear(new Color(6577));
|
||||
Gfx.draw_square(touch.x() - 8, touch.y() - 8, 16, 16, new Color(65535), new Color(13271));
|
||||
|
||||
let a = 10;
|
||||
let b = 15;
|
||||
let total = a + b;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user