asset addressable PBS surface
This commit is contained in:
parent
b75b2a5825
commit
7c9f7b58db
@ -0,0 +1,42 @@
|
|||||||
|
package p.studio.compiler.pbs;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
|
||||||
|
public final class PbsCallableSlotCounter {
|
||||||
|
private PbsCallableSlotCounter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int returnSlots(
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType) {
|
||||||
|
return switch (returnKind) {
|
||||||
|
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||||
|
case RESULT -> 1;
|
||||||
|
case PLAIN -> Math.max(0, typeSlots(returnType));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int typeSlots(final PbsAst.TypeRef typeRef) {
|
||||||
|
final var unwrapped = unwrapGroup(typeRef);
|
||||||
|
if (unwrapped == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return switch (unwrapped.kind()) {
|
||||||
|
case UNIT, ERROR -> 0;
|
||||||
|
case NAMED_TUPLE -> unwrapped.fields().stream()
|
||||||
|
.mapToInt(field -> Math.max(1, typeSlots(field.typeRef())))
|
||||||
|
.sum();
|
||||||
|
default -> 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PbsAst.TypeRef unwrapGroup(final PbsAst.TypeRef typeRef) {
|
||||||
|
if (typeRef == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeRef.kind() != PbsAst.TypeRefKind.GROUP) {
|
||||||
|
return typeRef;
|
||||||
|
}
|
||||||
|
return unwrapGroup(typeRef.inner());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -523,6 +523,8 @@ public final class PbsFrontendCompiler {
|
|||||||
String callableName,
|
String callableName,
|
||||||
int arity,
|
int arity,
|
||||||
int returnSlots,
|
int returnSlots,
|
||||||
|
PbsAst.ReturnKind returnKind,
|
||||||
|
PbsAst.TypeRef returnType,
|
||||||
String shapeSurface) {
|
String shapeSurface) {
|
||||||
public ImportedCallableSurface {
|
public ImportedCallableSurface {
|
||||||
moduleId = moduleId == null ? ModuleId.none() : moduleId;
|
moduleId = moduleId == null ? ModuleId.none() : moduleId;
|
||||||
|
|||||||
@ -76,10 +76,7 @@ public final class PbsReservedMetadataExtractor {
|
|||||||
abiModule,
|
abiModule,
|
||||||
abiMethod,
|
abiMethod,
|
||||||
abiVersion,
|
abiVersion,
|
||||||
switch (signature.returnKind()) {
|
PbsCallableSlotCounter.returnSlots(signature.returnKind(), signature.returnType()),
|
||||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
|
||||||
case PLAIN, RESULT -> 1;
|
|
||||||
},
|
|
||||||
assetLoweringAttribute.isPresent()
|
assetLoweringAttribute.isPresent()
|
||||||
? safeToInteger(longArgument(assetLoweringAttribute.get(), "param").orElse(-1L))
|
? safeToInteger(longArgument(assetLoweringAttribute.get(), "param").orElse(-1L))
|
||||||
: null,
|
: null,
|
||||||
@ -129,10 +126,7 @@ public final class PbsReservedMetadataExtractor {
|
|||||||
canonicalIntrinsicName(canonicalTypeName, intrinsicName),
|
canonicalIntrinsicName(canonicalTypeName, intrinsicName),
|
||||||
longArgument(intrinsicMetadata, "version").orElse(canonicalVersion),
|
longArgument(intrinsicMetadata, "version").orElse(canonicalVersion),
|
||||||
signature.parameters().size(),
|
signature.parameters().size(),
|
||||||
switch (signature.returnKind()) {
|
PbsCallableSlotCounter.returnSlots(signature.returnKind(), signature.returnType()),
|
||||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
|
||||||
case PLAIN, RESULT -> 1;
|
|
||||||
},
|
|
||||||
signature.span()));
|
signature.span()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.compiler.pbs.lowering;
|
package p.studio.compiler.pbs.lowering;
|
||||||
|
|
||||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||||
|
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
|
import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
|
||||||
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
||||||
@ -87,10 +88,7 @@ final class PbsExecutableBodyLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||||
return switch (functionDecl.returnKind()) {
|
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
|
||||||
case PLAIN, RESULT -> 1;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finalizeFunctionInstructions(
|
private void finalizeFunctionInstructions(
|
||||||
@ -155,10 +153,13 @@ final class PbsExecutableBodyLowerer {
|
|||||||
final PbsAst.LetStatement letStatement,
|
final PbsAst.LetStatement letStatement,
|
||||||
final PbsExecutableLoweringContext context) {
|
final PbsExecutableLoweringContext context) {
|
||||||
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(letStatement.initializer(), context);
|
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(letStatement.initializer(), context);
|
||||||
|
final var tupleProjection = tupleProjectionOf(letStatement.initializer(), context);
|
||||||
|
final var valueSlots = Math.max(1, materializedValueSlots(letStatement.initializer(), context));
|
||||||
lowerExpression(letStatement.initializer(), context);
|
lowerExpression(letStatement.initializer(), context);
|
||||||
final var localSlot = context.declareLocalSlot(letStatement.name());
|
final var localSlot = context.declareLocalSlots(letStatement.name(), valueSlots);
|
||||||
context.bindLocalOwner(letStatement.name(), localOwner);
|
context.bindLocalOwner(letStatement.name(), localOwner);
|
||||||
emitSetLocal(localSlot, letStatement.span(), context);
|
context.bindLocalTupleProjection(letStatement.name(), tupleProjection);
|
||||||
|
emitStoreLocalSlots(localSlot, valueSlots, letStatement.span(), context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,10 +183,13 @@ final class PbsExecutableBodyLowerer {
|
|||||||
}
|
}
|
||||||
if (assignStatement.operator() == PbsAst.AssignOperator.ASSIGN) {
|
if (assignStatement.operator() == PbsAst.AssignOperator.ASSIGN) {
|
||||||
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(assignStatement.value(), context);
|
final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(assignStatement.value(), context);
|
||||||
|
final var tupleProjection = tupleProjectionOf(assignStatement.value(), context);
|
||||||
|
final var valueSlots = Math.max(1, materializedValueSlots(assignStatement.value(), context));
|
||||||
lowerExpression(assignStatement.value(), context);
|
lowerExpression(assignStatement.value(), context);
|
||||||
if (targetLocalSlot != null) {
|
if (targetLocalSlot != null) {
|
||||||
context.bindLocalOwner(target.rootName(), localOwner);
|
context.bindLocalOwner(target.rootName(), localOwner);
|
||||||
emitSetLocal(targetLocalSlot, assignStatement.span(), context);
|
context.bindLocalTupleProjection(target.rootName(), tupleProjection);
|
||||||
|
emitStoreLocalSlots(targetLocalSlot, valueSlots, assignStatement.span(), context);
|
||||||
} else {
|
} else {
|
||||||
emitSetGlobal(targetGlobalSlot, assignStatement.span(), context);
|
emitSetGlobal(targetGlobalSlot, assignStatement.span(), context);
|
||||||
}
|
}
|
||||||
@ -476,6 +480,17 @@ final class PbsExecutableBodyLowerer {
|
|||||||
emitPushI32(assetId, memberExpr.span(), context);
|
emitPushI32(assetId, memberExpr.span(), context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (memberExpr.receiver() instanceof PbsAst.IdentifierExpr identifierExpr) {
|
||||||
|
final var tupleProjection = context.resolveLocalTupleProjection(identifierExpr.name());
|
||||||
|
final var localSlot = context.resolveLocalSlot(identifierExpr.name());
|
||||||
|
if (tupleProjection != null && localSlot != null) {
|
||||||
|
final var fieldProjection = tupleProjection.fieldsByLabel().get(memberExpr.memberName());
|
||||||
|
if (fieldProjection != null) {
|
||||||
|
emitLoadLocalSlots(localSlot + fieldProjection.slotOffset(), fieldProjection.slotCount(), memberExpr.span(), context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
lowerExpression(memberExpr.receiver(), context);
|
lowerExpression(memberExpr.receiver(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,7 +549,12 @@ final class PbsExecutableBodyLowerer {
|
|||||||
}
|
}
|
||||||
final var slot = context.localSlotByNameId().get(context.nameTable().register(identifierExpr.name()));
|
final var slot = context.localSlotByNameId().get(context.nameTable().register(identifierExpr.name()));
|
||||||
if (slot != null) {
|
if (slot != null) {
|
||||||
emitGetLocal(slot, identifierExpr.span(), context);
|
final var tupleProjection = context.resolveLocalTupleProjection(identifierExpr.name());
|
||||||
|
if (tupleProjection != null) {
|
||||||
|
emitLoadLocalSlots(slot, tupleProjection.slotCount(), identifierExpr.span(), context);
|
||||||
|
} else {
|
||||||
|
emitGetLocal(slot, identifierExpr.span(), context);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final var globalSlot = context.resolveGlobalSlot(identifierExpr.name());
|
final var globalSlot = context.resolveGlobalSlot(identifierExpr.name());
|
||||||
@ -589,6 +609,15 @@ final class PbsExecutableBodyLowerer {
|
|||||||
if (assetReference != null && context.resolveAssetId(assetReference) != null) {
|
if (assetReference != null && context.resolveAssetId(assetReference) != null) {
|
||||||
yield 1;
|
yield 1;
|
||||||
}
|
}
|
||||||
|
if (memberExpr.receiver() instanceof PbsAst.IdentifierExpr identifierExpr) {
|
||||||
|
final var tupleProjection = context.resolveLocalTupleProjection(identifierExpr.name());
|
||||||
|
if (tupleProjection != null) {
|
||||||
|
final var fieldProjection = tupleProjection.fieldsByLabel().get(memberExpr.memberName());
|
||||||
|
if (fieldProjection != null) {
|
||||||
|
yield fieldProjection.slotCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
yield materializedValueSlots(memberExpr.receiver(), context);
|
yield materializedValueSlots(memberExpr.receiver(), context);
|
||||||
}
|
}
|
||||||
case PbsAst.PropagateExpr propagateExpr -> materializedValueSlots(propagateExpr.expression(), context);
|
case PbsAst.PropagateExpr propagateExpr -> materializedValueSlots(propagateExpr.expression(), context);
|
||||||
@ -605,7 +634,7 @@ final class PbsExecutableBodyLowerer {
|
|||||||
yield slots;
|
yield slots;
|
||||||
}
|
}
|
||||||
case PbsAst.BlockExpr blockExpr -> blockValueSlots(blockExpr.block(), context);
|
case PbsAst.BlockExpr blockExpr -> blockValueSlots(blockExpr.block(), context);
|
||||||
case PbsAst.IdentifierExpr identifierExpr -> producesIdentifierValue(identifierExpr, context) ? 1 : 0;
|
case PbsAst.IdentifierExpr identifierExpr -> identifierValueSlots(identifierExpr, context);
|
||||||
case PbsAst.IntLiteralExpr ignored -> 1;
|
case PbsAst.IntLiteralExpr ignored -> 1;
|
||||||
case PbsAst.StringLiteralExpr ignoredString -> 1;
|
case PbsAst.StringLiteralExpr ignoredString -> 1;
|
||||||
case PbsAst.BoolLiteralExpr ignoredBool -> 1;
|
case PbsAst.BoolLiteralExpr ignoredBool -> 1;
|
||||||
@ -648,6 +677,31 @@ final class PbsExecutableBodyLowerer {
|
|||||||
|| context.resolveConstDecl(identifierExpr.name()) != null;
|
|| context.resolveConstDecl(identifierExpr.name()) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int identifierValueSlots(
|
||||||
|
final PbsAst.IdentifierExpr identifierExpr,
|
||||||
|
final PbsExecutableLoweringContext context) {
|
||||||
|
if (!producesIdentifierValue(identifierExpr, context)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final var tupleProjection = context.resolveLocalTupleProjection(identifierExpr.name());
|
||||||
|
return tupleProjection == null ? 1 : tupleProjection.slotCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsTupleProjection tupleProjectionOf(
|
||||||
|
final PbsAst.Expression expression,
|
||||||
|
final PbsExecutableLoweringContext context) {
|
||||||
|
if (expression instanceof PbsAst.CallExpr callExpr) {
|
||||||
|
final var callableId = callsiteEmitter.resolveUniqueCallableId(callExpr, context);
|
||||||
|
if (callableId != null) {
|
||||||
|
return context.tupleProjectionByCallableId().get(callableId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expression instanceof PbsAst.IdentifierExpr identifierExpr) {
|
||||||
|
return context.resolveLocalTupleProjection(identifierExpr.name());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void lowerConstExpression(
|
private void lowerConstExpression(
|
||||||
final PbsAst.Expression expression,
|
final PbsAst.Expression expression,
|
||||||
final PbsExecutableLoweringContext context) {
|
final PbsExecutableLoweringContext context) {
|
||||||
@ -821,6 +875,16 @@ final class PbsExecutableBodyLowerer {
|
|||||||
span));
|
span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void emitStoreLocalSlots(
|
||||||
|
final int firstSlot,
|
||||||
|
final int slotCount,
|
||||||
|
final p.studio.compiler.source.Span span,
|
||||||
|
final PbsExecutableLoweringContext context) {
|
||||||
|
for (int i = slotCount - 1; i >= 0; i--) {
|
||||||
|
emitSetLocal(firstSlot + i, span, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void emitSetGlobal(
|
private void emitSetGlobal(
|
||||||
final int slot,
|
final int slot,
|
||||||
final p.studio.compiler.source.Span span,
|
final p.studio.compiler.source.Span span,
|
||||||
@ -935,6 +999,16 @@ final class PbsExecutableBodyLowerer {
|
|||||||
span));
|
span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void emitLoadLocalSlots(
|
||||||
|
final int firstSlot,
|
||||||
|
final int slotCount,
|
||||||
|
final p.studio.compiler.source.Span span,
|
||||||
|
final PbsExecutableLoweringContext context) {
|
||||||
|
for (int i = 0; i < slotCount; i++) {
|
||||||
|
emitGetLocal(firstSlot + i, span, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void emitGetGlobal(
|
private void emitGetGlobal(
|
||||||
final int slot,
|
final int slot,
|
||||||
final p.studio.compiler.source.Span span,
|
final p.studio.compiler.source.Span span,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package p.studio.compiler.pbs.lowering;
|
package p.studio.compiler.pbs.lowering;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||||
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
import p.studio.compiler.source.identifiers.CallableId;
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
@ -26,6 +27,7 @@ final class PbsExecutableCallableRegistryFactory {
|
|||||||
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 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();
|
||||||
final var localCallables = collectLocalLowerableCallables(ast);
|
final var localCallables = collectLocalLowerableCallables(ast);
|
||||||
@ -37,6 +39,7 @@ final class PbsExecutableCallableRegistryFactory {
|
|||||||
callableIdsByNameAndArity,
|
callableIdsByNameAndArity,
|
||||||
callableIdByDeclaration,
|
callableIdByDeclaration,
|
||||||
returnSlotsByCallableId,
|
returnSlotsByCallableId,
|
||||||
|
tupleProjectionByCallableId,
|
||||||
typeSurfaceTable,
|
typeSurfaceTable,
|
||||||
callableShapeTable,
|
callableShapeTable,
|
||||||
localCallables);
|
localCallables);
|
||||||
@ -45,6 +48,7 @@ final class PbsExecutableCallableRegistryFactory {
|
|||||||
callableIdTable,
|
callableIdTable,
|
||||||
callableIdsByNameAndArity,
|
callableIdsByNameAndArity,
|
||||||
returnSlotsByCallableId,
|
returnSlotsByCallableId,
|
||||||
|
tupleProjectionByCallableId,
|
||||||
importedCallables);
|
importedCallables);
|
||||||
|
|
||||||
final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>();
|
final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>();
|
||||||
@ -60,6 +64,7 @@ final class PbsExecutableCallableRegistryFactory {
|
|||||||
callableIdsByNameAndArity,
|
callableIdsByNameAndArity,
|
||||||
callableSignatureByCallableId,
|
callableSignatureByCallableId,
|
||||||
returnSlotsByCallableId,
|
returnSlotsByCallableId,
|
||||||
|
tupleProjectionByCallableId,
|
||||||
ReadOnlyList.wrap(callableSignatures));
|
ReadOnlyList.wrap(callableSignatures));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +91,7 @@ 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, PbsTupleProjection> tupleProjectionByCallableId,
|
||||||
final TypeSurfaceTable typeSurfaceTable,
|
final TypeSurfaceTable typeSurfaceTable,
|
||||||
final CallableShapeTable callableShapeTable,
|
final CallableShapeTable callableShapeTable,
|
||||||
final ReadOnlyList<PbsLowerableCallable> localCallables) {
|
final ReadOnlyList<PbsLowerableCallable> localCallables) {
|
||||||
@ -107,6 +113,10 @@ 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 tupleProjection = tupleProjectionOf(declaredFn.returnKind(), declaredFn.returnType());
|
||||||
|
if (tupleProjection != null) {
|
||||||
|
tupleProjectionByCallableId.put(callableId, tupleProjection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +125,7 @@ final class PbsExecutableCallableRegistryFactory {
|
|||||||
final CallableTable callableIdTable,
|
final CallableTable callableIdTable,
|
||||||
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 ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) {
|
final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) {
|
||||||
for (final var importedCallable : importedCallables) {
|
for (final var importedCallable : importedCallables) {
|
||||||
final var callableId = callableIdTable.register(
|
final var callableId = callableIdTable.register(
|
||||||
@ -130,13 +141,46 @@ 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 tupleProjection = tupleProjectionOf(importedCallable.returnKind(), importedCallable.returnType());
|
||||||
|
if (tupleProjection != null) {
|
||||||
|
tupleProjectionByCallableId.put(callableId, tupleProjection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||||
return switch (functionDecl.returnKind()) {
|
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
}
|
||||||
case PLAIN, RESULT -> 1;
|
|
||||||
};
|
private PbsTupleProjection tupleProjectionOf(
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType) {
|
||||||
|
if (returnKind != PbsAst.ReturnKind.PLAIN) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var unwrapped = unwrapGroup(returnType);
|
||||||
|
if (unwrapped == null || unwrapped.kind() != PbsAst.TypeRefKind.NAMED_TUPLE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var fieldsByLabel = new HashMap<String, PbsTupleFieldProjection>();
|
||||||
|
var slotOffset = 0;
|
||||||
|
for (final var field : unwrapped.fields()) {
|
||||||
|
final var fieldSlotCount = Math.max(1, PbsCallableSlotCounter.returnSlots(PbsAst.ReturnKind.PLAIN, field.typeRef()));
|
||||||
|
if (field.label() != null && !field.label().isBlank()) {
|
||||||
|
fieldsByLabel.put(field.label(), new PbsTupleFieldProjection(field.label(), slotOffset, fieldSlotCount));
|
||||||
|
}
|
||||||
|
slotOffset += fieldSlotCount;
|
||||||
|
}
|
||||||
|
return new PbsTupleProjection(slotOffset, Map.copyOf(fieldsByLabel));
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,23 @@ import java.util.Map;
|
|||||||
import static p.studio.compiler.pbs.lowering.PbsExecutableLoweringModels.*;
|
import static p.studio.compiler.pbs.lowering.PbsExecutableLoweringModels.*;
|
||||||
|
|
||||||
final class PbsExecutableCallsiteEmitter {
|
final class PbsExecutableCallsiteEmitter {
|
||||||
|
CallableId resolveUniqueCallableId(
|
||||||
|
final PbsAst.CallExpr callExpr,
|
||||||
|
final PbsExecutableLoweringContext context) {
|
||||||
|
final var calleeIdentity = resolveCalleeIdentity(callExpr.callee(), context.nameTable());
|
||||||
|
if (calleeIdentity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var candidates = resolveCallsiteCandidates(callExpr, calleeIdentity, context);
|
||||||
|
if (!candidates.hostCandidates().isEmpty() || !candidates.intrinsicCandidates().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (candidates.callableCandidates().size() != 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return candidates.callableCandidates().getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
IRReservedMetadata.HostMethodBinding resolveUniqueHostBinding(
|
IRReservedMetadata.HostMethodBinding resolveUniqueHostBinding(
|
||||||
final PbsAst.CallExpr callExpr,
|
final PbsAst.CallExpr callExpr,
|
||||||
final PbsExecutableLoweringContext context) {
|
final PbsExecutableLoweringContext context) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ final class PbsExecutableLoweringContext {
|
|||||||
private final PbsExecutableCallableRegistry callableRegistry;
|
private final PbsExecutableCallableRegistry callableRegistry;
|
||||||
private final IntrinsicTable intrinsicIdTable;
|
private final IntrinsicTable intrinsicIdTable;
|
||||||
private final Map<NameId, Integer> localSlotByNameId;
|
private final Map<NameId, Integer> localSlotByNameId;
|
||||||
|
private final Map<NameId, PbsTupleProjection> localTupleProjectionByNameId = new HashMap<>();
|
||||||
private final Map<NameId, String> localOwnerByNameId;
|
private final Map<NameId, String> localOwnerByNameId;
|
||||||
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
||||||
private final ArrayDeque<PbsLoopTargets> loopTargets = new ArrayDeque<>();
|
private final ArrayDeque<PbsLoopTargets> loopTargets = new ArrayDeque<>();
|
||||||
@ -95,6 +96,10 @@ final class PbsExecutableLoweringContext {
|
|||||||
return callableRegistry.returnSlotsByCallableId();
|
return callableRegistry.returnSlotsByCallableId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<p.studio.compiler.source.identifiers.CallableId, PbsTupleProjection> tupleProjectionByCallableId() {
|
||||||
|
return callableRegistry.tupleProjectionByCallableId();
|
||||||
|
}
|
||||||
|
|
||||||
IntrinsicTable intrinsicIdTable() {
|
IntrinsicTable intrinsicIdTable() {
|
||||||
return intrinsicIdTable;
|
return intrinsicIdTable;
|
||||||
}
|
}
|
||||||
@ -131,6 +136,13 @@ final class PbsExecutableLoweringContext {
|
|||||||
return localSlotByNameId.get(nameTable.register(localName));
|
return localSlotByNameId.get(nameTable.register(localName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PbsTupleProjection resolveLocalTupleProjection(final String localName) {
|
||||||
|
if (localName == null || localName.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return localTupleProjectionByNameId.get(nameTable.register(localName));
|
||||||
|
}
|
||||||
|
|
||||||
Integer resolveGlobalSlot(final String globalName) {
|
Integer resolveGlobalSlot(final String globalName) {
|
||||||
if (globalName == null || globalName.isBlank()) {
|
if (globalName == null || globalName.isBlank()) {
|
||||||
return null;
|
return null;
|
||||||
@ -154,16 +166,37 @@ final class PbsExecutableLoweringContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int declareLocalSlot(final String localName) {
|
int declareLocalSlot(final String localName) {
|
||||||
|
return declareLocalSlots(localName, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int declareLocalSlots(
|
||||||
|
final String localName,
|
||||||
|
final int slotCount) {
|
||||||
final var nameId = nameTable.register(localName);
|
final var nameId = nameTable.register(localName);
|
||||||
final var existing = localSlotByNameId.get(nameId);
|
final var existing = localSlotByNameId.get(nameId);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
final var slot = nextLocalSlot++;
|
final var slot = nextLocalSlot;
|
||||||
|
nextLocalSlot += Math.max(1, slotCount);
|
||||||
localSlotByNameId.put(nameId, slot);
|
localSlotByNameId.put(nameId, slot);
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bindLocalTupleProjection(
|
||||||
|
final String localName,
|
||||||
|
final PbsTupleProjection tupleProjection) {
|
||||||
|
if (localName == null || localName.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final var nameId = nameTable.register(localName);
|
||||||
|
if (tupleProjection == null) {
|
||||||
|
localTupleProjectionByNameId.remove(nameId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localTupleProjectionByNameId.put(nameId, tupleProjection);
|
||||||
|
}
|
||||||
|
|
||||||
int nextLocalSlot() {
|
int nextLocalSlot() {
|
||||||
return nextLocalSlot;
|
return nextLocalSlot;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,9 +58,21 @@ 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, PbsTupleProjection> tupleProjectionByCallableId,
|
||||||
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record PbsTupleProjection(
|
||||||
|
int slotCount,
|
||||||
|
Map<String, PbsTupleFieldProjection> fieldsByLabel) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record PbsTupleFieldProjection(
|
||||||
|
String label,
|
||||||
|
int slotOffset,
|
||||||
|
int slotCount) {
|
||||||
|
}
|
||||||
|
|
||||||
record PbsLoweredExecutableBody(
|
record PbsLoweredExecutableBody(
|
||||||
ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions,
|
ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions,
|
||||||
int localSlots,
|
int localSlots,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.compiler.services;
|
package p.studio.compiler.services;
|
||||||
|
|
||||||
import p.studio.compiler.models.IRReservedMetadata;
|
import p.studio.compiler.models.IRReservedMetadata;
|
||||||
|
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||||
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||||
import p.studio.compiler.pbs.PbsReservedMetadataExtractor;
|
import p.studio.compiler.pbs.PbsReservedMetadataExtractor;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
@ -93,6 +94,8 @@ final class PbsImportedSemanticContextService {
|
|||||||
localName + "." + method.name(),
|
localName + "." + method.name(),
|
||||||
method.parameters().size(),
|
method.parameters().size(),
|
||||||
returnSlotsFor(method),
|
returnSlotsFor(method),
|
||||||
|
method.returnKind(),
|
||||||
|
method.returnType(),
|
||||||
frontendCompiler.callableShapeSurfaceOf(method)));
|
frontendCompiler.callableShapeSurfaceOf(method)));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -106,6 +109,8 @@ final class PbsImportedSemanticContextService {
|
|||||||
localName,
|
localName,
|
||||||
functionDecl.parameters().size(),
|
functionDecl.parameters().size(),
|
||||||
returnSlotsFor(functionDecl),
|
returnSlotsFor(functionDecl),
|
||||||
|
functionDecl.returnKind(),
|
||||||
|
functionDecl.returnType(),
|
||||||
frontendCompiler.callableShapeSurfaceOf(functionDecl)));
|
frontendCompiler.callableShapeSurfaceOf(functionDecl)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -445,10 +450,7 @@ final class PbsImportedSemanticContextService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||||
return switch (functionDecl.returnKind()) {
|
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
|
||||||
case PLAIN, RESULT -> 1;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String importItemLocalName(final PbsAst.ImportItem importItem) {
|
private String importItemLocalName(final PbsAst.ImportItem importItem) {
|
||||||
|
|||||||
@ -16,3 +16,21 @@ declare host LowAssets {
|
|||||||
[Capability(name = "asset")]
|
[Capability(name = "asset")]
|
||||||
fn cancel(loading_handle: int) -> int;
|
fn cancel(loading_handle: int) -> int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare service Assets {
|
||||||
|
fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int) {
|
||||||
|
return LowAssets.load(addressable, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(loading_handle: int) -> int {
|
||||||
|
return LowAssets.status(loading_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(loading_handle: int) -> int {
|
||||||
|
return LowAssets.commit(loading_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(loading_handle: int) -> int {
|
||||||
|
return LowAssets.cancel(loading_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
pub host LowAssets;
|
mod host LowAssets;
|
||||||
|
pub service Assets;
|
||||||
|
|||||||
@ -470,6 +470,7 @@ class PbsFrontendCompilerTest {
|
|||||||
&& h.abiModule().equals("asset")
|
&& h.abiModule().equals("asset")
|
||||||
&& h.abiMethod().equals("load")
|
&& h.abiMethod().equals("load")
|
||||||
&& h.abiVersion() == 1
|
&& h.abiVersion() == 1
|
||||||
|
&& h.retSlots() == 2
|
||||||
&& java.util.Objects.equals(h.assetLoweringParam(), 0)));
|
&& java.util.Objects.equals(h.assetLoweringParam(), 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -87,11 +87,11 @@ class PbsGateUSdkInterfaceConformanceTest {
|
|||||||
import { Color } from @core:color;
|
import { Color } from @core:color;
|
||||||
import { Gfx } from @sdk:gfx;
|
import { Gfx } from @sdk:gfx;
|
||||||
import { Input, InputPad, InputButton } from @sdk:input;
|
import { Input, InputPad, InputButton } from @sdk:input;
|
||||||
import { LowAssets } from @sdk:asset;
|
import { Assets } from @sdk:asset;
|
||||||
import { Log } from @sdk:log;
|
import { Log } from @sdk:log;
|
||||||
|
|
||||||
declare contract Renderer {
|
declare contract Renderer {
|
||||||
fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: LowAssets, log: Log) -> void;
|
fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void;
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
"pub contract Renderer;",
|
"pub contract Renderer;",
|
||||||
@ -127,7 +127,8 @@ class PbsGateUSdkInterfaceConformanceTest {
|
|||||||
.anyMatch(h -> h.ownerName().equals("LowAssets")
|
.anyMatch(h -> h.ownerName().equals("LowAssets")
|
||||||
&& h.abiModule().equals("asset")
|
&& h.abiModule().equals("asset")
|
||||||
&& h.abiMethod().equals("load")
|
&& h.abiMethod().equals("load")
|
||||||
&& h.abiVersion() == 1));
|
&& h.abiVersion() == 1
|
||||||
|
&& java.util.Objects.equals(h.assetLoweringParam(), 0)));
|
||||||
|
|
||||||
final var negative = compileWorkspaceModule(
|
final var negative = compileWorkspaceModule(
|
||||||
tempDir.resolve("gate-u-reserved-import-negative"),
|
tempDir.resolve("gate-u-reserved-import-negative"),
|
||||||
@ -365,7 +366,8 @@ class PbsGateUSdkInterfaceConformanceTest {
|
|||||||
&& java.util.Objects.equals(h.assetLoweringParam(), 0)
|
&& java.util.Objects.equals(h.assetLoweringParam(), 0)
|
||||||
&& h.abiModule().equals("asset")
|
&& h.abiModule().equals("asset")
|
||||||
&& h.abiMethod().equals("load")
|
&& h.abiMethod().equals("load")
|
||||||
&& h.abiVersion() == 1));
|
&& h.abiVersion() == 1
|
||||||
|
&& h.retSlots() == 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorkspaceCompileResult compileWorkspaceModule(
|
private WorkspaceCompileResult compileWorkspaceModule(
|
||||||
|
|||||||
@ -908,9 +908,9 @@ class PBSFrontendPhaseServiceTest {
|
|||||||
final var sourceFile = modulePath.resolve("source.pbs");
|
final var sourceFile = modulePath.resolve("source.pbs");
|
||||||
final var modBarrel = modulePath.resolve("mod.barrel");
|
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||||
Files.writeString(sourceFile, """
|
Files.writeString(sourceFile, """
|
||||||
import { LowAssets } from @sdk:asset;
|
import { Assets } from @sdk:asset;
|
||||||
declare contract Loader {
|
declare contract Loader {
|
||||||
fn load(assets: LowAssets) -> void;
|
fn load(assets: Assets) -> void;
|
||||||
}
|
}
|
||||||
""");
|
""");
|
||||||
Files.writeString(modBarrel, "pub contract Loader;");
|
Files.writeString(modBarrel, "pub contract Loader;");
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
package p.studio.compiler.workspaces;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import p.studio.compiler.messages.Addressable;
|
||||||
|
import p.studio.compiler.messages.FESurfaceContext;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class AssetSurfaceContextLoader {
|
||||||
|
private static final Path REGISTRY_PATH = Path.of("assets", ".prometeu", "index.json");
|
||||||
|
|
||||||
|
private final ObjectMapper mapper;
|
||||||
|
|
||||||
|
public AssetSurfaceContextLoader() {
|
||||||
|
this(new ObjectMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetSurfaceContextLoader(final ObjectMapper mapper) {
|
||||||
|
this.mapper = Objects.requireNonNull(mapper, "mapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
public FESurfaceContext load(final Path projectRoot) {
|
||||||
|
final var registryPath = Objects.requireNonNull(projectRoot, "projectRoot")
|
||||||
|
.toAbsolutePath()
|
||||||
|
.normalize()
|
||||||
|
.resolve(REGISTRY_PATH);
|
||||||
|
if (!Files.isRegularFile(registryPath)) {
|
||||||
|
return FESurfaceContext.empty();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final var document = mapper.readValue(registryPath.toFile(), RegistryDocument.class);
|
||||||
|
final var addressables = new ArrayList<Addressable>();
|
||||||
|
if (document.assets != null) {
|
||||||
|
for (final var asset : document.assets) {
|
||||||
|
if (asset == null || !isIncludedInBuild(asset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addressables.add(new Addressable(toAddress(asset.root()), asset.assetId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FESurfaceContext(ReadOnlyList.wrap(addressables));
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new IllegalStateException("Unable to load asset surface registry: " + registryPath, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isIncludedInBuild(final RegistryAssetDocument asset) {
|
||||||
|
return asset.includedInBuild() == null || asset.includedInBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toAddress(final String root) {
|
||||||
|
final var normalizedRoot = normalizeRoot(root);
|
||||||
|
if (normalizedRoot.isBlank()) {
|
||||||
|
throw new IllegalStateException("Asset root must not be blank in surface registry");
|
||||||
|
}
|
||||||
|
return "assets." + normalizedRoot.replace('/', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeRoot(final String root) {
|
||||||
|
if (root == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
final var trimmed = root.trim().replace('\\', '/');
|
||||||
|
final List<String> segments = new ArrayList<>();
|
||||||
|
for (final var rawSegment : trimmed.split("/")) {
|
||||||
|
final var segment = rawSegment.trim();
|
||||||
|
if (segment.isEmpty() || ".".equals(segment)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ("..".equals(segment)) {
|
||||||
|
throw new IllegalStateException("Asset root escapes trusted boundary: " + root);
|
||||||
|
}
|
||||||
|
segments.add(segment);
|
||||||
|
}
|
||||||
|
return String.join("/", segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private record RegistryDocument(
|
||||||
|
@JsonProperty("assets") List<RegistryAssetDocument> assets) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private record RegistryAssetDocument(
|
||||||
|
@JsonProperty("asset_id") int assetId,
|
||||||
|
@JsonProperty("root") String root,
|
||||||
|
@JsonProperty("included_in_build") Boolean includedInBuild) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,12 +5,15 @@ import p.studio.compiler.FrontendRegistryService;
|
|||||||
import p.studio.compiler.messages.BuildingIssueSink;
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
import p.studio.compiler.messages.FrontendPhaseContext;
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
import p.studio.compiler.models.BuilderPipelineContext;
|
import p.studio.compiler.models.BuilderPipelineContext;
|
||||||
|
import p.studio.compiler.workspaces.AssetSurfaceContextLoader;
|
||||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
import p.studio.compiler.workspaces.PipelineStage;
|
import p.studio.compiler.workspaces.PipelineStage;
|
||||||
import p.studio.utilities.logs.LogAggregator;
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FrontendPhasePipelineStage implements PipelineStage {
|
public class FrontendPhasePipelineStage implements PipelineStage {
|
||||||
|
private final AssetSurfaceContextLoader assetSurfaceContextLoader = new AssetSurfaceContextLoader();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BuildingIssueSink run(final BuilderPipelineContext ctx, final LogAggregator logs) {
|
public BuildingIssueSink run(final BuilderPipelineContext ctx, final LogAggregator logs) {
|
||||||
final var frontedSpec = ctx.resolvedWorkspace.frontendSpec();
|
final var frontedSpec = ctx.resolvedWorkspace.frontendSpec();
|
||||||
@ -27,7 +30,9 @@ public class FrontendPhasePipelineStage implements PipelineStage {
|
|||||||
projectTable,
|
projectTable,
|
||||||
fileTable,
|
fileTable,
|
||||||
ctx.resolvedWorkspace.stack(),
|
ctx.resolvedWorkspace.stack(),
|
||||||
ctx.resolvedWorkspace.stdlib());
|
ctx.resolvedWorkspace.stdlib(),
|
||||||
|
p.studio.compiler.messages.HostAdmissionContext.permissiveDefault(),
|
||||||
|
assetSurfaceContextLoader.load(ctx.resolvedWorkspace.mainProject().getRootPath()));
|
||||||
final var diagnostics = DiagnosticSink.empty();
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
final var issues = BuildingIssueSink.empty();
|
final var issues = BuildingIssueSink.empty();
|
||||||
ctx.irBackend = service.get().compile(frontendPhaseContext, diagnostics, logs, issues);
|
ctx.irBackend = service.get().compile(frontendPhaseContext, diagnostics, logs, issues);
|
||||||
|
|||||||
@ -16,8 +16,6 @@ class BackendClaimScopeSpecTest {
|
|||||||
"docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md";
|
"docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md";
|
||||||
private static final String MATRIX_RELATIVE_PATH =
|
private static final String MATRIX_RELATIVE_PATH =
|
||||||
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
|
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
|
||||||
private static final String DECISION_RELATIVE_PATH =
|
|
||||||
"docs/compiler/pbs/decisions/SPAWN-YIELD v1 Claim Rescope Decision.md";
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void loweringSpecMustDeclareSpawnYieldOutsideCoreV1ClaimScope() throws IOException {
|
void loweringSpecMustDeclareSpawnYieldOutsideCoreV1ClaimScope() throws IOException {
|
||||||
@ -43,16 +41,6 @@ class BackendClaimScopeSpecTest {
|
|||||||
"G20-9.4.3 row must include explicit claim rescope note");
|
"G20-9.4.3 row must include explicit claim rescope note");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void decisionRecordMustExistForSpawnYieldClaimRescope() throws IOException {
|
|
||||||
final var decisionPath = locateRepoRoot().resolve(DECISION_RELATIVE_PATH);
|
|
||||||
assertTrue(Files.isRegularFile(decisionPath),
|
|
||||||
"claim rescope decision is missing: " + decisionPath);
|
|
||||||
final var content = Files.readString(decisionPath);
|
|
||||||
assertTrue(content.contains("Trilha B"),
|
|
||||||
"decision must record official Track B claim rescope choice");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<String> findRequirementRow(
|
private Optional<String> findRequirementRow(
|
||||||
final List<String> lines,
|
final List<String> lines,
|
||||||
final String requirementId) {
|
final String requirementId) {
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
package p.studio.compiler.workspaces;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class AssetSurfaceContextLoaderTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path tempDir;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldLoadOnlyIncludedAssetsAsAddressables() throws IOException {
|
||||||
|
final var registryDir = tempDir.resolve("assets").resolve(".prometeu");
|
||||||
|
Files.createDirectories(registryDir);
|
||||||
|
Files.writeString(registryDir.resolve("index.json"), """
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"next_asset_id": 4,
|
||||||
|
"assets": [
|
||||||
|
{ "asset_id": 3, "root": "ui/atlas2", "included_in_build": true },
|
||||||
|
{ "asset_id": 7, "root": "ui/one-more-atlas", "included_in_build": false },
|
||||||
|
{ "asset_id": 8, "root": "ui/sound" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
|
||||||
|
final var surface = new AssetSurfaceContextLoader().load(tempDir);
|
||||||
|
|
||||||
|
assertEquals(2, surface.assets().size());
|
||||||
|
assertEquals("assets.ui.atlas2", surface.assets().get(0).address());
|
||||||
|
assertEquals(3, surface.assets().get(0).assetId());
|
||||||
|
assertEquals("assets.ui.sound", surface.assets().get(1).address());
|
||||||
|
assertEquals(8, surface.assets().get(1).assetId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnEmptySurfaceWhenRegistryIsMissing() {
|
||||||
|
final var surface = new AssetSurfaceContextLoader().load(tempDir);
|
||||||
|
|
||||||
|
assertTrue(surface.assets().isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,114 @@
|
|||||||
[ {
|
[ {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "8 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan diagnostics updated.",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: bla",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: one-more-atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: ui_atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: one-more-atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: bbb2",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: ui_atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: Bigode",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: Zelda",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "8 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan diagnostics updated.",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: bla",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: one-more-atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: ui_atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: one-more-atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: bbb2",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: ui_atlas",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: Bigode",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Discovered asset: Zelda",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
"source" : "Assets",
|
"source" : "Assets",
|
||||||
"message" : "Asset scan diagnostics updated.",
|
"message" : "Asset scan diagnostics updated.",
|
||||||
"severity" : "INFO",
|
"severity" : "INFO",
|
||||||
@ -2388,114 +2498,4 @@
|
|||||||
"message" : "Asset scan diagnostics updated.",
|
"message" : "Asset scan diagnostics updated.",
|
||||||
"severity" : "INFO",
|
"severity" : "INFO",
|
||||||
"sticky" : false
|
"sticky" : false
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: bla",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: one-more-atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: ui_atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: one-more-atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: bbb2",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: ui_atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: Bigode",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Asset scan started",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "7 assets loaded",
|
|
||||||
"severity" : "SUCCESS",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Asset scan diagnostics updated.",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: bla",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: one-more-atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: ui_atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: one-more-atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: bbb2",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: ui_atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: Bigode",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Asset scan started",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "7 assets loaded",
|
|
||||||
"severity" : "SUCCESS",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Asset scan diagnostics updated.",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: bla",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
}, {
|
|
||||||
"source" : "Assets",
|
|
||||||
"message" : "Discovered asset: one-more-atlas",
|
|
||||||
"severity" : "INFO",
|
|
||||||
"sticky" : false
|
|
||||||
} ]
|
} ]
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -3,14 +3,55 @@ import { Color } from @core:color;
|
|||||||
import { Log } from @sdk:log;
|
import { Log } from @sdk:log;
|
||||||
import { Input } from @sdk:input;
|
import { Input } from @sdk:input;
|
||||||
import { Gfx } from @sdk:gfx;
|
import { Gfx } from @sdk:gfx;
|
||||||
|
import { Assets } from @sdk:asset;
|
||||||
|
|
||||||
|
declare global loading_handle: int = -1;
|
||||||
|
declare global loading_status: int = -1;
|
||||||
declare global frame_counter: int = 0;
|
declare global frame_counter: int = 0;
|
||||||
declare global tile_id: int = 0;
|
declare global tile_id: int = 0;
|
||||||
declare const MAX_FRAMES: int = 4;
|
declare const MAX_FRAMES: int = 4;
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
fn init() -> void
|
||||||
|
{
|
||||||
|
loading_handle= -1;
|
||||||
|
loading_status = -1;
|
||||||
|
frame_counter = 0;
|
||||||
|
tile_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
[Frame]
|
[Frame]
|
||||||
fn frame() -> void
|
fn frame() -> void
|
||||||
{
|
{
|
||||||
|
Gfx.clear(new Color(6577));
|
||||||
|
|
||||||
|
if (loading_handle == -1) {
|
||||||
|
let t = Assets.load(assets.ui.atlas2, 3);
|
||||||
|
if (t.status != 0) {
|
||||||
|
Log.error("load failed");
|
||||||
|
} else {
|
||||||
|
loading_handle = t.loading_handle;
|
||||||
|
Log.info("state: loading");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let s = Assets.status(loading_handle);
|
||||||
|
if (s == 2) {
|
||||||
|
let commit_status = Assets.commit(loading_handle);
|
||||||
|
if (commit_status != 0) {
|
||||||
|
Log.error("commit failed");
|
||||||
|
}
|
||||||
|
} else if (s == 3) {
|
||||||
|
let sprite_status = Gfx.set_sprite(3, 10, 150, 150, 0, 0, true, false, false, 1);
|
||||||
|
if (sprite_status != 0) {
|
||||||
|
Log.error("set_sprite failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.info("state: waiting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let touch = Input.touch();
|
let touch = Input.touch();
|
||||||
|
|
||||||
if (touch.button().released())
|
if (touch.button().released())
|
||||||
@ -33,10 +74,8 @@ fn frame() -> void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx.clear(new Color(6577));
|
Gfx.set_sprite(0, 0, touch.x() - 16, touch.y() + 8, tile_id, 0, true, true, false, 0);
|
||||||
// Gfx.draw_square(touch.x(), touch.y(), 16, 16, new Color(65535), new Color(13271));
|
Gfx.set_sprite(0, 1, touch.x() + 16, touch.y() + 8, tile_id, 0, true, false, false, 0);
|
||||||
let sprite_status = Gfx.set_sprite(0, 0, touch.x() - 16, touch.y() + 8, tile_id, 0, true, true, false, 0);
|
|
||||||
let sprite_status2 = Gfx.set_sprite(0, 1, touch.x() + 16, touch.y() + 8, tile_id, 0, true, false, false, 0);
|
|
||||||
|
|
||||||
let a = 10;
|
let a = 10;
|
||||||
let b = 15;
|
let b = 15;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user