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,
|
||||
int arity,
|
||||
int returnSlots,
|
||||
PbsAst.ReturnKind returnKind,
|
||||
PbsAst.TypeRef returnType,
|
||||
String shapeSurface) {
|
||||
public ImportedCallableSurface {
|
||||
moduleId = moduleId == null ? ModuleId.none() : moduleId;
|
||||
|
||||
@ -76,10 +76,7 @@ public final class PbsReservedMetadataExtractor {
|
||||
abiModule,
|
||||
abiMethod,
|
||||
abiVersion,
|
||||
switch (signature.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
},
|
||||
PbsCallableSlotCounter.returnSlots(signature.returnKind(), signature.returnType()),
|
||||
assetLoweringAttribute.isPresent()
|
||||
? safeToInteger(longArgument(assetLoweringAttribute.get(), "param").orElse(-1L))
|
||||
: null,
|
||||
@ -129,10 +126,7 @@ public final class PbsReservedMetadataExtractor {
|
||||
canonicalIntrinsicName(canonicalTypeName, intrinsicName),
|
||||
longArgument(intrinsicMetadata, "version").orElse(canonicalVersion),
|
||||
signature.parameters().size(),
|
||||
switch (signature.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
},
|
||||
PbsCallableSlotCounter.returnSlots(signature.returnKind(), signature.returnType()),
|
||||
signature.span()));
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio.compiler.pbs.lowering;
|
||||
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
|
||||
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
||||
@ -87,10 +88,7 @@ final class PbsExecutableBodyLowerer {
|
||||
}
|
||||
|
||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||
return switch (functionDecl.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
};
|
||||
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||
}
|
||||
|
||||
private void finalizeFunctionInstructions(
|
||||
@ -155,10 +153,13 @@ final class PbsExecutableBodyLowerer {
|
||||
final PbsAst.LetStatement letStatement,
|
||||
final PbsExecutableLoweringContext 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);
|
||||
final var localSlot = context.declareLocalSlot(letStatement.name());
|
||||
final var localSlot = context.declareLocalSlots(letStatement.name(), valueSlots);
|
||||
context.bindLocalOwner(letStatement.name(), localOwner);
|
||||
emitSetLocal(localSlot, letStatement.span(), context);
|
||||
context.bindLocalTupleProjection(letStatement.name(), tupleProjection);
|
||||
emitStoreLocalSlots(localSlot, valueSlots, letStatement.span(), context);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -182,10 +183,13 @@ final class PbsExecutableBodyLowerer {
|
||||
}
|
||||
if (assignStatement.operator() == PbsAst.AssignOperator.ASSIGN) {
|
||||
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);
|
||||
if (targetLocalSlot != null) {
|
||||
context.bindLocalOwner(target.rootName(), localOwner);
|
||||
emitSetLocal(targetLocalSlot, assignStatement.span(), context);
|
||||
context.bindLocalTupleProjection(target.rootName(), tupleProjection);
|
||||
emitStoreLocalSlots(targetLocalSlot, valueSlots, assignStatement.span(), context);
|
||||
} else {
|
||||
emitSetGlobal(targetGlobalSlot, assignStatement.span(), context);
|
||||
}
|
||||
@ -476,6 +480,17 @@ final class PbsExecutableBodyLowerer {
|
||||
emitPushI32(assetId, memberExpr.span(), context);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -534,7 +549,12 @@ final class PbsExecutableBodyLowerer {
|
||||
}
|
||||
final var slot = context.localSlotByNameId().get(context.nameTable().register(identifierExpr.name()));
|
||||
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;
|
||||
}
|
||||
final var globalSlot = context.resolveGlobalSlot(identifierExpr.name());
|
||||
@ -589,6 +609,15 @@ final class PbsExecutableBodyLowerer {
|
||||
if (assetReference != null && context.resolveAssetId(assetReference) != null) {
|
||||
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);
|
||||
}
|
||||
case PbsAst.PropagateExpr propagateExpr -> materializedValueSlots(propagateExpr.expression(), context);
|
||||
@ -605,7 +634,7 @@ final class PbsExecutableBodyLowerer {
|
||||
yield slots;
|
||||
}
|
||||
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.StringLiteralExpr ignoredString -> 1;
|
||||
case PbsAst.BoolLiteralExpr ignoredBool -> 1;
|
||||
@ -648,6 +677,31 @@ final class PbsExecutableBodyLowerer {
|
||||
|| 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(
|
||||
final PbsAst.Expression expression,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
@ -821,6 +875,16 @@ final class PbsExecutableBodyLowerer {
|
||||
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(
|
||||
final int slot,
|
||||
final p.studio.compiler.source.Span span,
|
||||
@ -935,6 +999,16 @@ final class PbsExecutableBodyLowerer {
|
||||
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(
|
||||
final int slot,
|
||||
final p.studio.compiler.source.Span span,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package p.studio.compiler.pbs.lowering;
|
||||
|
||||
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.source.identifiers.CallableId;
|
||||
@ -26,6 +27,7 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
final var callableIdsByNameAndArity = new HashMap<PbsCallableResolutionKey, List<CallableId>>();
|
||||
final var callableIdByDeclaration = new HashMap<PbsLowerableCallable, CallableId>();
|
||||
final var returnSlotsByCallableId = new HashMap<CallableId, Integer>();
|
||||
final var tupleProjectionByCallableId = new HashMap<CallableId, PbsTupleProjection>();
|
||||
final var typeSurfaceTable = new TypeSurfaceTable();
|
||||
final var callableShapeTable = new CallableShapeTable();
|
||||
final var localCallables = collectLocalLowerableCallables(ast);
|
||||
@ -37,6 +39,7 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
callableIdsByNameAndArity,
|
||||
callableIdByDeclaration,
|
||||
returnSlotsByCallableId,
|
||||
tupleProjectionByCallableId,
|
||||
typeSurfaceTable,
|
||||
callableShapeTable,
|
||||
localCallables);
|
||||
@ -45,6 +48,7 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
callableIdTable,
|
||||
callableIdsByNameAndArity,
|
||||
returnSlotsByCallableId,
|
||||
tupleProjectionByCallableId,
|
||||
importedCallables);
|
||||
|
||||
final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>();
|
||||
@ -60,6 +64,7 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
callableIdsByNameAndArity,
|
||||
callableSignatureByCallableId,
|
||||
returnSlotsByCallableId,
|
||||
tupleProjectionByCallableId,
|
||||
ReadOnlyList.wrap(callableSignatures));
|
||||
}
|
||||
|
||||
@ -86,6 +91,7 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
final Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
final Map<PbsLowerableCallable, CallableId> callableIdByDeclaration,
|
||||
final Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
|
||||
final TypeSurfaceTable typeSurfaceTable,
|
||||
final CallableShapeTable callableShapeTable,
|
||||
final ReadOnlyList<PbsLowerableCallable> localCallables) {
|
||||
@ -107,6 +113,10 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
ignored -> new ArrayList<>())
|
||||
.add(callableId);
|
||||
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 Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
final Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
final Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
|
||||
final ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables) {
|
||||
for (final var importedCallable : importedCallables) {
|
||||
final var callableId = callableIdTable.register(
|
||||
@ -130,13 +141,46 @@ final class PbsExecutableCallableRegistryFactory {
|
||||
ignored -> new ArrayList<>())
|
||||
.add(callableId);
|
||||
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) {
|
||||
return switch (functionDecl.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
};
|
||||
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||
}
|
||||
|
||||
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.*;
|
||||
|
||||
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(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final PbsExecutableLoweringContext context) {
|
||||
|
||||
@ -18,6 +18,7 @@ final class PbsExecutableLoweringContext {
|
||||
private final PbsExecutableCallableRegistry callableRegistry;
|
||||
private final IntrinsicTable intrinsicIdTable;
|
||||
private final Map<NameId, Integer> localSlotByNameId;
|
||||
private final Map<NameId, PbsTupleProjection> localTupleProjectionByNameId = new HashMap<>();
|
||||
private final Map<NameId, String> localOwnerByNameId;
|
||||
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
||||
private final ArrayDeque<PbsLoopTargets> loopTargets = new ArrayDeque<>();
|
||||
@ -95,6 +96,10 @@ final class PbsExecutableLoweringContext {
|
||||
return callableRegistry.returnSlotsByCallableId();
|
||||
}
|
||||
|
||||
Map<p.studio.compiler.source.identifiers.CallableId, PbsTupleProjection> tupleProjectionByCallableId() {
|
||||
return callableRegistry.tupleProjectionByCallableId();
|
||||
}
|
||||
|
||||
IntrinsicTable intrinsicIdTable() {
|
||||
return intrinsicIdTable;
|
||||
}
|
||||
@ -131,6 +136,13 @@ final class PbsExecutableLoweringContext {
|
||||
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) {
|
||||
if (globalName == null || globalName.isBlank()) {
|
||||
return null;
|
||||
@ -154,16 +166,37 @@ final class PbsExecutableLoweringContext {
|
||||
}
|
||||
|
||||
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 existing = localSlotByNameId.get(nameId);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
final var slot = nextLocalSlot++;
|
||||
final var slot = nextLocalSlot;
|
||||
nextLocalSlot += Math.max(1, slotCount);
|
||||
localSlotByNameId.put(nameId, 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() {
|
||||
return nextLocalSlot;
|
||||
}
|
||||
|
||||
@ -58,9 +58,21 @@ public class PbsExecutableLoweringModels {
|
||||
Map<PbsCallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
Map<CallableId, CallableSignatureRef> callableSignatureByCallableId,
|
||||
Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
Map<CallableId, PbsTupleProjection> tupleProjectionByCallableId,
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||
}
|
||||
|
||||
record PbsTupleProjection(
|
||||
int slotCount,
|
||||
Map<String, PbsTupleFieldProjection> fieldsByLabel) {
|
||||
}
|
||||
|
||||
record PbsTupleFieldProjection(
|
||||
String label,
|
||||
int slotOffset,
|
||||
int slotCount) {
|
||||
}
|
||||
|
||||
record PbsLoweredExecutableBody(
|
||||
ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions,
|
||||
int localSlots,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio.compiler.services;
|
||||
|
||||
import p.studio.compiler.models.IRReservedMetadata;
|
||||
import p.studio.compiler.pbs.PbsCallableSlotCounter;
|
||||
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||
import p.studio.compiler.pbs.PbsReservedMetadataExtractor;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
@ -93,6 +94,8 @@ final class PbsImportedSemanticContextService {
|
||||
localName + "." + method.name(),
|
||||
method.parameters().size(),
|
||||
returnSlotsFor(method),
|
||||
method.returnKind(),
|
||||
method.returnType(),
|
||||
frontendCompiler.callableShapeSurfaceOf(method)));
|
||||
}
|
||||
continue;
|
||||
@ -106,6 +109,8 @@ final class PbsImportedSemanticContextService {
|
||||
localName,
|
||||
functionDecl.parameters().size(),
|
||||
returnSlotsFor(functionDecl),
|
||||
functionDecl.returnKind(),
|
||||
functionDecl.returnType(),
|
||||
frontendCompiler.callableShapeSurfaceOf(functionDecl)));
|
||||
continue;
|
||||
}
|
||||
@ -445,10 +450,7 @@ final class PbsImportedSemanticContextService {
|
||||
}
|
||||
|
||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||
return switch (functionDecl.returnKind()) {
|
||||
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||
case PLAIN, RESULT -> 1;
|
||||
};
|
||||
return PbsCallableSlotCounter.returnSlots(functionDecl.returnKind(), functionDecl.returnType());
|
||||
}
|
||||
|
||||
private String importItemLocalName(final PbsAst.ImportItem importItem) {
|
||||
|
||||
@ -16,3 +16,21 @@ declare host LowAssets {
|
||||
[Capability(name = "asset")]
|
||||
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.abiMethod().equals("load")
|
||||
&& h.abiVersion() == 1
|
||||
&& h.retSlots() == 2
|
||||
&& java.util.Objects.equals(h.assetLoweringParam(), 0)));
|
||||
}
|
||||
|
||||
|
||||
@ -87,11 +87,11 @@ class PbsGateUSdkInterfaceConformanceTest {
|
||||
import { Color } from @core:color;
|
||||
import { Gfx } from @sdk:gfx;
|
||||
import { Input, InputPad, InputButton } from @sdk:input;
|
||||
import { LowAssets } from @sdk:asset;
|
||||
import { Assets } from @sdk:asset;
|
||||
import { Log } from @sdk:log;
|
||||
|
||||
declare contract Renderer {
|
||||
fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: LowAssets, log: Log) -> void;
|
||||
fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void;
|
||||
}
|
||||
""",
|
||||
"pub contract Renderer;",
|
||||
@ -127,7 +127,8 @@ class PbsGateUSdkInterfaceConformanceTest {
|
||||
.anyMatch(h -> h.ownerName().equals("LowAssets")
|
||||
&& h.abiModule().equals("asset")
|
||||
&& h.abiMethod().equals("load")
|
||||
&& h.abiVersion() == 1));
|
||||
&& h.abiVersion() == 1
|
||||
&& java.util.Objects.equals(h.assetLoweringParam(), 0)));
|
||||
|
||||
final var negative = compileWorkspaceModule(
|
||||
tempDir.resolve("gate-u-reserved-import-negative"),
|
||||
@ -365,7 +366,8 @@ class PbsGateUSdkInterfaceConformanceTest {
|
||||
&& java.util.Objects.equals(h.assetLoweringParam(), 0)
|
||||
&& h.abiModule().equals("asset")
|
||||
&& h.abiMethod().equals("load")
|
||||
&& h.abiVersion() == 1));
|
||||
&& h.abiVersion() == 1
|
||||
&& h.retSlots() == 2));
|
||||
}
|
||||
|
||||
private WorkspaceCompileResult compileWorkspaceModule(
|
||||
|
||||
@ -908,9 +908,9 @@ class PBSFrontendPhaseServiceTest {
|
||||
final var sourceFile = modulePath.resolve("source.pbs");
|
||||
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||
Files.writeString(sourceFile, """
|
||||
import { LowAssets } from @sdk:asset;
|
||||
import { Assets } from @sdk:asset;
|
||||
declare contract Loader {
|
||||
fn load(assets: LowAssets) -> void;
|
||||
fn load(assets: Assets) -> void;
|
||||
}
|
||||
""");
|
||||
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.FrontendPhaseContext;
|
||||
import p.studio.compiler.models.BuilderPipelineContext;
|
||||
import p.studio.compiler.workspaces.AssetSurfaceContextLoader;
|
||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||
import p.studio.compiler.workspaces.PipelineStage;
|
||||
import p.studio.utilities.logs.LogAggregator;
|
||||
|
||||
@Slf4j
|
||||
public class FrontendPhasePipelineStage implements PipelineStage {
|
||||
private final AssetSurfaceContextLoader assetSurfaceContextLoader = new AssetSurfaceContextLoader();
|
||||
|
||||
@Override
|
||||
public BuildingIssueSink run(final BuilderPipelineContext ctx, final LogAggregator logs) {
|
||||
final var frontedSpec = ctx.resolvedWorkspace.frontendSpec();
|
||||
@ -27,7 +30,9 @@ public class FrontendPhasePipelineStage implements PipelineStage {
|
||||
projectTable,
|
||||
fileTable,
|
||||
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 issues = BuildingIssueSink.empty();
|
||||
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";
|
||||
private static final String MATRIX_RELATIVE_PATH =
|
||||
"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
|
||||
void loweringSpecMustDeclareSpawnYieldOutsideCoreV1ClaimScope() throws IOException {
|
||||
@ -43,16 +41,6 @@ class BackendClaimScopeSpecTest {
|
||||
"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(
|
||||
final List<String> lines,
|
||||
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",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
@ -2388,114 +2498,4 @@
|
||||
"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
|
||||
}, {
|
||||
"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 { Input } from @sdk:input;
|
||||
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 tile_id: int = 0;
|
||||
declare const MAX_FRAMES: int = 4;
|
||||
|
||||
[Init]
|
||||
fn init() -> void
|
||||
{
|
||||
loading_handle= -1;
|
||||
loading_status = -1;
|
||||
frame_counter = 0;
|
||||
tile_id = 0;
|
||||
}
|
||||
|
||||
[Frame]
|
||||
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();
|
||||
|
||||
if (touch.button().released())
|
||||
@ -33,10 +74,8 @@ fn frame() -> void
|
||||
}
|
||||
}
|
||||
|
||||
Gfx.clear(new Color(6577));
|
||||
// Gfx.draw_square(touch.x(), touch.y(), 16, 16, new Color(65535), new Color(13271));
|
||||
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);
|
||||
Gfx.set_sprite(0, 0, touch.x() - 16, touch.y() + 8, tile_id, 0, true, true, false, 0);
|
||||
Gfx.set_sprite(0, 1, touch.x() + 16, touch.y() + 8, tile_id, 0, true, false, false, 0);
|
||||
|
||||
let a = 10;
|
||||
let b = 15;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user