implements PR-O1.2
This commit is contained in:
parent
b5c372efb2
commit
9e3d9ccb93
@ -23,7 +23,22 @@ public record IRBackendExecutableFunction(
|
|||||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||||
moduleKey = moduleKey == null ? "" : moduleKey;
|
moduleKey = moduleKey == null ? "" : moduleKey;
|
||||||
callableName = Objects.requireNonNull(callableName, "callableName");
|
callableName = Objects.requireNonNull(callableName, "callableName");
|
||||||
|
if (callableName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("callableName must not be blank");
|
||||||
|
}
|
||||||
|
if (sourceStart < 0 || sourceEnd < 0 || sourceEnd < sourceStart) {
|
||||||
|
throw new IllegalArgumentException("invalid source span bounds");
|
||||||
|
}
|
||||||
|
if (paramSlots < 0 || localSlots < 0 || returnSlots < 0 || maxStackSlots < 0) {
|
||||||
|
throw new IllegalArgumentException("slots must be non-negative");
|
||||||
|
}
|
||||||
|
if (maxStackSlots < returnSlots) {
|
||||||
|
throw new IllegalArgumentException("maxStackSlots must be >= returnSlots");
|
||||||
|
}
|
||||||
instructions = instructions == null ? ReadOnlyList.empty() : instructions;
|
instructions = instructions == null ? ReadOnlyList.empty() : instructions;
|
||||||
|
for (final var instruction : instructions) {
|
||||||
|
Objects.requireNonNull(instruction, "instruction");
|
||||||
|
}
|
||||||
span = span == null ? Span.none() : span;
|
span = span == null ? Span.none() : span;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,29 +48,62 @@ public record IRBackendExecutableFunction(
|
|||||||
String calleeCallableName,
|
String calleeCallableName,
|
||||||
HostCallMetadata hostCall,
|
HostCallMetadata hostCall,
|
||||||
IntrinsicCallMetadata intrinsicCall,
|
IntrinsicCallMetadata intrinsicCall,
|
||||||
|
Integer expectedArgSlots,
|
||||||
|
Integer expectedRetSlots,
|
||||||
Span span) {
|
Span span) {
|
||||||
|
public Instruction(
|
||||||
|
final InstructionKind kind,
|
||||||
|
final String calleeModuleKey,
|
||||||
|
final String calleeCallableName,
|
||||||
|
final HostCallMetadata hostCall,
|
||||||
|
final IntrinsicCallMetadata intrinsicCall,
|
||||||
|
final Span span) {
|
||||||
|
this(kind, calleeModuleKey, calleeCallableName, hostCall, intrinsicCall, null, null, span);
|
||||||
|
}
|
||||||
|
|
||||||
public Instruction {
|
public Instruction {
|
||||||
Objects.requireNonNull(kind, "kind");
|
Objects.requireNonNull(kind, "kind");
|
||||||
span = span == null ? Span.none() : span;
|
span = span == null ? Span.none() : span;
|
||||||
calleeModuleKey = calleeModuleKey == null ? "" : calleeModuleKey;
|
calleeModuleKey = calleeModuleKey == null ? "" : calleeModuleKey;
|
||||||
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
|
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
|
||||||
|
if (expectedArgSlots != null && expectedArgSlots < 0) {
|
||||||
|
throw new IllegalArgumentException("expectedArgSlots must be non-negative");
|
||||||
|
}
|
||||||
|
if (expectedRetSlots != null && expectedRetSlots < 0) {
|
||||||
|
throw new IllegalArgumentException("expectedRetSlots must be non-negative");
|
||||||
|
}
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case CALL_FUNC -> {
|
case CALL_FUNC -> {
|
||||||
if (calleeCallableName.isBlank()) {
|
if (calleeCallableName.isBlank()) {
|
||||||
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableName");
|
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableName");
|
||||||
}
|
}
|
||||||
|
if (hostCall != null || intrinsicCall != null) {
|
||||||
|
throw new IllegalArgumentException("CALL_FUNC must not carry host or intrinsic metadata");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case CALL_HOST -> {
|
case CALL_HOST -> {
|
||||||
if (hostCall == null) {
|
if (hostCall == null) {
|
||||||
throw new IllegalArgumentException("CALL_HOST requires hostCall metadata");
|
throw new IllegalArgumentException("CALL_HOST requires hostCall metadata");
|
||||||
}
|
}
|
||||||
|
if (intrinsicCall != null) {
|
||||||
|
throw new IllegalArgumentException("CALL_HOST must not carry intrinsic metadata");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case CALL_INTRINSIC -> {
|
case CALL_INTRINSIC -> {
|
||||||
if (intrinsicCall == null) {
|
if (intrinsicCall == null) {
|
||||||
throw new IllegalArgumentException("CALL_INTRINSIC requires intrinsic metadata");
|
throw new IllegalArgumentException("CALL_INTRINSIC requires intrinsic metadata");
|
||||||
}
|
}
|
||||||
|
if (hostCall != null) {
|
||||||
|
throw new IllegalArgumentException("CALL_INTRINSIC must not carry host metadata");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case HALT, RET -> {
|
case HALT, RET -> {
|
||||||
|
if (!calleeCallableName.isBlank() || hostCall != null || intrinsicCall != null) {
|
||||||
|
throw new IllegalArgumentException(kind + " must not carry callsite metadata");
|
||||||
|
}
|
||||||
|
if (expectedArgSlots != null || expectedRetSlots != null) {
|
||||||
|
throw new IllegalArgumentException(kind + " must not carry expected slot metadata");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,6 +126,12 @@ public record IRBackendExecutableFunction(
|
|||||||
public HostCallMetadata {
|
public HostCallMetadata {
|
||||||
module = Objects.requireNonNull(module, "module");
|
module = Objects.requireNonNull(module, "module");
|
||||||
name = Objects.requireNonNull(name, "name");
|
name = Objects.requireNonNull(name, "name");
|
||||||
|
if (module.isBlank() || name.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("module and name must not be blank");
|
||||||
|
}
|
||||||
|
if (version < 0 || argSlots < 0 || retSlots < 0) {
|
||||||
|
throw new IllegalArgumentException("host metadata values must be non-negative");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +141,12 @@ public record IRBackendExecutableFunction(
|
|||||||
int intrinsicId) {
|
int intrinsicId) {
|
||||||
public IntrinsicCallMetadata {
|
public IntrinsicCallMetadata {
|
||||||
canonicalName = Objects.requireNonNull(canonicalName, "canonicalName");
|
canonicalName = Objects.requireNonNull(canonicalName, "canonicalName");
|
||||||
|
if (canonicalName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("canonicalName must not be blank");
|
||||||
|
}
|
||||||
|
if (canonicalVersion < 0) {
|
||||||
|
throw new IllegalArgumentException("canonicalVersion must be non-negative");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import p.studio.utilities.structures.ReadOnlyList;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class IRBackendExecutableContractTest {
|
class IRBackendExecutableContractTest {
|
||||||
|
|
||||||
@ -30,6 +31,54 @@ class IRBackendExecutableContractTest {
|
|||||||
assertEquals(IRBackendExecutableFunction.InstructionKind.CALL_FUNC, callFunc.kind());
|
assertEquals(IRBackendExecutableFunction.InstructionKind.CALL_FUNC, callFunc.kind());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void functionContractMustRejectInvalidSlotAndSpanBounds() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new IRBackendExecutableFunction(
|
||||||
|
new FileId(1),
|
||||||
|
"app",
|
||||||
|
"main",
|
||||||
|
10,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
Span.none()));
|
||||||
|
|
||||||
|
final var thrown = assertThrows(IllegalArgumentException.class, () -> new IRBackendExecutableFunction(
|
||||||
|
new FileId(1),
|
||||||
|
"app",
|
||||||
|
"main",
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
Span.none()));
|
||||||
|
assertTrue(thrown.getMessage().contains("maxStackSlots"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void instructionContractMustRejectMixedMetadataKinds() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
"app/main",
|
||||||
|
"foo",
|
||||||
|
new IRBackendExecutableFunction.HostCallMetadata("gfx", "draw", 1, 0, 0),
|
||||||
|
null,
|
||||||
|
Span.none()));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new IRBackendExecutableFunction.HostCallMetadata(
|
||||||
|
"gfx",
|
||||||
|
"draw",
|
||||||
|
1,
|
||||||
|
-1,
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void aggregatorMustPreserveExecutableFunctionOrderDeterministically() {
|
void aggregatorMustPreserveExecutableFunctionOrderDeterministically() {
|
||||||
final var fileA = new IRBackendFile(
|
final var fileA = new IRBackendFile(
|
||||||
@ -87,4 +136,3 @@ class IRBackendExecutableContractTest {
|
|||||||
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
|
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user