compiler: Encode package headers in checked code

603eae1b1a1f47dceae10c30cc7af8a6f624a19f2c9df86fb30f17af77e76716
Alexis Sellier committed ago 1 parent 1de1349d
lib/std/lang/il/binary/decodeTests.rad +21 -0
12 12
static MEMORY: [u8; 2048] = [0; 2048];
13 13
14 14
/// Structural limits for small test programs.
15 15
constant LIMITS: binary::Limits = binary::Limits { registers: 16, blocks: 4 };
16 16
17 +
/// Check exact-fit package encoding and every shorter output window.
18 +
unsafe fn encodingCapacity(package: &binary::Package, expected: &[u8]) throws (testing::TestError) {
19 +
    for capacity in 0..expected.len + 1 {
20 +
        let mut buffer: [u8; 2050] = [0xa5; 2050];
21 +
        let mut failed = false;
22 +
        let count = try program::encode(&mut buffer[1..capacity + 1], package) catch err {
23 +
            assert err == binary::Error::Capacity;
24 +
            set failed = true;
25 +
            0
26 +
        };
27 +
        assert failed == (capacity < expected.len);
28 +
        assert buffer[0] == 0xa5;
29 +
        for i in capacity + 1..buffer.len { assert buffer[i] == 0xa5; }
30 +
        if not failed {
31 +
            assert count == expected.len;
32 +
            try testing::expectBytesEq(&buffer[1..count + 1], expected);
33 +
        }
34 +
    }
35 +
}
36 +
17 37
/// Check a package round trip and failure at every truncated byte length.
18 38
unsafe fn roundTrip(package: &binary::Package) throws (testing::TestError) {
19 39
    let mut buffer: [u8; 2048] = [0; 2048];
20 40
    let length = try program::encode(&mut buffer[..], package) catch {
21 41
        throw testing::TestError::Failed;
22 42
    };
43 +
    try encodingCapacity(package, &buffer[..length]);
23 44
    let memory = &mut MEMORY[..2048];
24 45
    let mut arena = alloc::new(&mut memory[..]);
25 46
    set arena.offset = 8;
26 47
    let decoded = try program::decode(&buffer[..length], &mut arena, LIMITS) catch {
27 48
        throw testing::TestError::Failed;
lib/std/lang/il/binary/program.rad +37 -25
32 32
            try writer::dataValue(out, value);
33 33
        }
34 34
    }
35 35
}
36 36
37 +
/// Write package identity, symbol tables, exports, and the optional entry.
38 +
fn writeHeader 'buffer (
39 +
    out: &mut writer::Writer 'buffer,
40 +
    symbols: &[*[u8]],
41 +
    name: *[u8],
42 +
    dependencies: &[*[u8]],
43 +
    exports: &[binary::Export],
44 +
    entry: ?*[u8]
45 +
) throws (binary::Error) {
46 +
    try writer::integer(out, binary::MAGIC as u64, 4);
47 +
    try writer::integer(out, binary::VERSION as u64, 4);
48 +
    try writer::integer(out, symbols.len as u64, 4);
49 +
    for name in symbols {
50 +
        try writer::bytes(out, name);
51 +
    }
52 +
    try writer::symbol(out, name);
53 +
    try writer::integer(out, dependencies.len as u64, 4);
54 +
    for name in dependencies {
55 +
        try writer::symbol(out, name);
56 +
    }
57 +
    try writer::integer(out, exports.len as u64, 4);
58 +
    for item in exports {
59 +
        try writer::symbol(out, item.name);
60 +
        match item.kind {
61 +
            case binary::ExportKind::Function => try writer::integer(out, 0, 1),
62 +
            case binary::ExportKind::Data => try writer::integer(out, 1, 1),
63 +
        }
64 +
    }
65 +
    if let entry = entry {
66 +
        try writer::integer(out, 1, 1);
67 +
        try writer::symbol(out, entry);
68 +
    } else {
69 +
        try writer::integer(out, 0, 1);
70 +
    }
71 +
}
72 +
37 73
/// Write a package. Return the number of encoded bytes.
38 74
/// All package tables and IL storage must remain valid during encoding.
39 75
export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) {
40 76
    let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in {
41 77
        let mut out = writer::new(output, symbols);
42 -
        try writer::integer(&mut out, binary::MAGIC as u64, 4);
43 -
        try writer::integer(&mut out, binary::VERSION as u64, 4);
44 -
        try writer::integer(&mut out, package.symbols.len as u64, 4);
45 -
        for name in package.symbols {
46 -
            try writer::bytes(&mut out, name);
47 -
        }
48 -
        try writer::symbol(&mut out, package.name);
49 -
        try writer::integer(&mut out, package.dependencies.len as u64, 4);
50 -
        for name in package.dependencies {
51 -
            try writer::symbol(&mut out, name);
52 -
        }
53 -
        try writer::integer(&mut out, package.exports.len as u64, 4);
54 -
        for item in package.exports {
55 -
            try writer::symbol(&mut out, item.name);
56 -
            match item.kind {
57 -
                case binary::ExportKind::Function => try writer::integer(&mut out, 0, 1),
58 -
                case binary::ExportKind::Data => try writer::integer(&mut out, 1, 1),
59 -
            }
60 -
        }
61 -
        if let entry = package.entry {
62 -
            try writer::integer(&mut out, 1, 1);
63 -
            try writer::symbol(&mut out, entry);
64 -
        } else {
65 -
            try writer::integer(&mut out, 0, 1);
66 -
        }
78 +
        try writeHeader(&mut out, symbols, package.name, package.dependencies, package.exports, package.entry);
67 79
        try writeData(&mut out, &package.program.data[..]);
68 80
        try writer::integer(&mut out, package.program.fns.len as u64, 4);
69 81
        for func in package.program.fns {
70 82
            try writer::symbol(&mut out, func.name);
71 83
            try writer::typ(&mut out, func.returnType);