compiler: Borrow binary writer storage by lexical region
44316af275bbf7a97136a5f38cdb43407d186d5184daa119fe015c66b7b97a45
1 parent
d98e577a
lib/std/lang/il/binary/program.rad
+57 -55
| 8 | 8 | use std::lang::il::binary::writer; |
|
| 9 | 9 | use std::lang::alloc; |
|
| 10 | 10 | use std::mem; |
|
| 11 | 11 | ||
| 12 | 12 | /// Write a counted sequence of typed SSA parameters. |
|
| 13 | - | unsafe fn writeParams(out: &mut writer::Writer, params: &[il::Param]) throws (binary::Error) { |
|
| 13 | + | fn writeParams 'buffer (out: &mut writer::Writer 'buffer, params: &[il::Param]) throws (binary::Error) { |
|
| 14 | 14 | try writer::integer(out, params.len as u64, 4); |
|
| 15 | 15 | for param in params { |
|
| 16 | 16 | try writer::integer(out, param.value.n as u64, 4); |
|
| 17 | 17 | try writer::typ(out, param.type); |
|
| 18 | 18 | } |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Write a package. Return the number of encoded bytes. |
|
| 22 | 22 | /// All package tables and IL storage must remain valid during encoding. |
|
| 23 | 23 | export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) { |
|
| 24 | - | let mut out = writer::new(bytes, package.symbols); |
|
| 25 | - | try writer::integer(&mut out, binary::MAGIC as u64, 4); |
|
| 26 | - | try writer::integer(&mut out, binary::VERSION as u64, 4); |
|
| 27 | - | try writer::integer(&mut out, package.symbols.len as u64, 4); |
|
| 28 | - | for name in package.symbols { |
|
| 29 | - | try writer::bytes(&mut out, name); |
|
| 30 | - | } |
|
| 31 | - | try writer::symbol(&mut out, package.name); |
|
| 32 | - | try writer::integer(&mut out, package.dependencies.len as u64, 4); |
|
| 33 | - | for name in package.dependencies { |
|
| 34 | - | try writer::symbol(&mut out, name); |
|
| 35 | - | } |
|
| 36 | - | try writer::integer(&mut out, package.exports.len as u64, 4); |
|
| 37 | - | for item in package.exports { |
|
| 38 | - | try writer::symbol(&mut out, item.name); |
|
| 39 | - | match item.kind { |
|
| 40 | - | case binary::ExportKind::Function => try writer::integer(&mut out, 0, 1), |
|
| 41 | - | case binary::ExportKind::Data => try writer::integer(&mut out, 1, 1), |
|
| 24 | + | let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in { |
|
| 25 | + | let mut out = writer::new(output, symbols); |
|
| 26 | + | try writer::integer(&mut out, binary::MAGIC as u64, 4); |
|
| 27 | + | try writer::integer(&mut out, binary::VERSION as u64, 4); |
|
| 28 | + | try writer::integer(&mut out, package.symbols.len as u64, 4); |
|
| 29 | + | for name in package.symbols { |
|
| 30 | + | try writer::bytes(&mut out, name); |
|
| 42 | 31 | } |
|
| 43 | - | } |
|
| 44 | - | if let entry = package.entry { |
|
| 45 | - | try writer::integer(&mut out, 1, 1); |
|
| 46 | - | try writer::symbol(&mut out, entry); |
|
| 47 | - | } else { |
|
| 48 | - | try writer::integer(&mut out, 0, 1); |
|
| 49 | - | } |
|
| 50 | - | try writer::integer(&mut out, package.program.data.len as u64, 4); |
|
| 51 | - | for item in package.program.data { |
|
| 52 | - | try writer::symbol(&mut out, item.name); |
|
| 53 | - | try writer::integer(&mut out, item.size as u64, 4); |
|
| 54 | - | try writer::integer(&mut out, item.alignment as u64, 4); |
|
| 55 | - | try writer::integer(&mut out, 1 if item.readOnly else 0, 1); |
|
| 56 | - | try writer::integer(&mut out, 1 if item.isZeroInit else 0, 1); |
|
| 57 | - | try writer::integer(&mut out, item.values.len as u64, 4); |
|
| 58 | - | for value in item.values { |
|
| 59 | - | try writer::dataValue(&mut out, value); |
|
| 32 | + | try writer::symbol(&mut out, package.name); |
|
| 33 | + | try writer::integer(&mut out, package.dependencies.len as u64, 4); |
|
| 34 | + | for name in package.dependencies { |
|
| 35 | + | try writer::symbol(&mut out, name); |
|
| 60 | 36 | } |
|
| 61 | - | } |
|
| 62 | - | try writer::integer(&mut out, package.program.fns.len as u64, 4); |
|
| 63 | - | for func in package.program.fns { |
|
| 64 | - | try writer::symbol(&mut out, func.name); |
|
| 65 | - | try writer::typ(&mut out, func.returnType); |
|
| 66 | - | try writer::integer(&mut out, 1 if func.isExtern else 0, 1); |
|
| 67 | - | try writeParams(&mut out, func.params); |
|
| 68 | - | try writer::integer(&mut out, func.blocks.len as u64, 4); |
|
| 69 | - | for block in func.blocks { |
|
| 70 | - | try writer::bytes(&mut out, block.label); |
|
| 71 | - | try writeParams(&mut out, block.params); |
|
| 72 | - | try writer::integer(&mut out, block.loopDepth as u64, 4); |
|
| 73 | - | try writer::integer(&mut out, block.preds.len as u64, 4); |
|
| 74 | - | for pred in block.preds { |
|
| 75 | - | try writer::integer(&mut out, pred as u64, 4); |
|
| 37 | + | try writer::integer(&mut out, package.exports.len as u64, 4); |
|
| 38 | + | for item in package.exports { |
|
| 39 | + | try writer::symbol(&mut out, item.name); |
|
| 40 | + | match item.kind { |
|
| 41 | + | case binary::ExportKind::Function => try writer::integer(&mut out, 0, 1), |
|
| 42 | + | case binary::ExportKind::Data => try writer::integer(&mut out, 1, 1), |
|
| 76 | 43 | } |
|
| 77 | - | try writer::integer(&mut out, block.instrs.len as u64, 4); |
|
| 78 | - | for instr in block.instrs { |
|
| 79 | - | try writer::instr(&mut out, instr); |
|
| 44 | + | } |
|
| 45 | + | if let entry = package.entry { |
|
| 46 | + | try writer::integer(&mut out, 1, 1); |
|
| 47 | + | try writer::symbol(&mut out, entry); |
|
| 48 | + | } else { |
|
| 49 | + | try writer::integer(&mut out, 0, 1); |
|
| 50 | + | } |
|
| 51 | + | try writer::integer(&mut out, package.program.data.len as u64, 4); |
|
| 52 | + | for item in package.program.data { |
|
| 53 | + | try writer::symbol(&mut out, item.name); |
|
| 54 | + | try writer::integer(&mut out, item.size as u64, 4); |
|
| 55 | + | try writer::integer(&mut out, item.alignment as u64, 4); |
|
| 56 | + | try writer::integer(&mut out, 1 if item.readOnly else 0, 1); |
|
| 57 | + | try writer::integer(&mut out, 1 if item.isZeroInit else 0, 1); |
|
| 58 | + | try writer::integer(&mut out, item.values.len as u64, 4); |
|
| 59 | + | for value in item.values { |
|
| 60 | + | try writer::dataValue(&mut out, value); |
|
| 61 | + | } |
|
| 62 | + | } |
|
| 63 | + | try writer::integer(&mut out, package.program.fns.len as u64, 4); |
|
| 64 | + | for func in package.program.fns { |
|
| 65 | + | try writer::symbol(&mut out, func.name); |
|
| 66 | + | try writer::typ(&mut out, func.returnType); |
|
| 67 | + | try writer::integer(&mut out, 1 if func.isExtern else 0, 1); |
|
| 68 | + | try writeParams(&mut out, func.params); |
|
| 69 | + | try writer::integer(&mut out, func.blocks.len as u64, 4); |
|
| 70 | + | for block in func.blocks { |
|
| 71 | + | try writer::bytes(&mut out, block.label); |
|
| 72 | + | try writeParams(&mut out, block.params); |
|
| 73 | + | try writer::integer(&mut out, block.loopDepth as u64, 4); |
|
| 74 | + | try writer::integer(&mut out, block.preds.len as u64, 4); |
|
| 75 | + | for pred in block.preds { |
|
| 76 | + | try writer::integer(&mut out, pred as u64, 4); |
|
| 77 | + | } |
|
| 78 | + | try writer::integer(&mut out, block.instrs.len as u64, 4); |
|
| 79 | + | for instr in block.instrs { |
|
| 80 | + | try writer::instr(&mut out, instr); |
|
| 81 | + | } |
|
| 80 | 82 | } |
|
| 81 | 83 | } |
|
| 84 | + | return out.offset; |
|
| 82 | 85 | } |
|
| 83 | - | return out.offset; |
|
| 84 | 86 | } |
|
| 85 | 87 | ||
| 86 | 88 | /// Read typed SSA parameters with checked register indices. |
|
| 87 | 89 | unsafe fn readParams(input: &mut reader::Reader) -> *unsafe [il::Param] throws (binary::Error) { |
|
| 88 | 90 | let n = try reader::count(input, 5); |
lib/std/lang/il/binary/tests.rad
+187 -161
| 9 | 9 | ||
| 10 | 10 | /// Decode arena backing storage. Tests reset it before each use. |
|
| 11 | 11 | static MEMORY: [u8; 512] = [0; 512]; |
|
| 12 | 12 | ||
| 13 | 13 | /// Check little-endian encoding for every integer width. |
|
| 14 | - | @test unsafe fn integers() throws (testing::TestError) { |
|
| 14 | + | @test fn integers() throws (testing::TestError) { |
|
| 15 | 15 | let mut buffer: [u8; 15] = [0; 15]; |
|
| 16 | - | let mut out = writer::new(&mut buffer[..], &[]); |
|
| 17 | - | try writer::integer(&mut out, 0x12, 1) catch { |
|
| 18 | - | throw testing::TestError::Failed; |
|
| 19 | - | }; |
|
| 20 | - | try writer::integer(&mut out, 0x3456, 2) catch { |
|
| 21 | - | throw testing::TestError::Failed; |
|
| 22 | - | }; |
|
| 23 | - | try writer::integer(&mut out, 0x789abcde, 4) catch { |
|
| 24 | - | throw testing::TestError::Failed; |
|
| 25 | - | }; |
|
| 26 | - | try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { |
|
| 27 | - | throw testing::TestError::Failed; |
|
| 28 | - | }; |
|
| 29 | - | try testing::expectBytesEq(&buffer[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd, |
|
| 30 | - | 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]); |
|
| 16 | + | let namesTable: [*[u8]; 0] = []; |
|
| 17 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 18 | + | let mut out = writer::new(storage, names); |
|
| 19 | + | try writer::integer(&mut out, 0x12, 1) catch { |
|
| 20 | + | throw testing::TestError::Failed; |
|
| 21 | + | }; |
|
| 22 | + | try writer::integer(&mut out, 0x3456, 2) catch { |
|
| 23 | + | throw testing::TestError::Failed; |
|
| 24 | + | }; |
|
| 25 | + | try writer::integer(&mut out, 0x789abcde, 4) catch { |
|
| 26 | + | throw testing::TestError::Failed; |
|
| 27 | + | }; |
|
| 28 | + | try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { |
|
| 29 | + | throw testing::TestError::Failed; |
|
| 30 | + | }; |
|
| 31 | + | try testing::expectBytesEq(&out.bytes[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd, |
|
| 32 | + | 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]); |
|
| 33 | + | } |
|
| 31 | 34 | } |
|
| 32 | 35 | ||
| 33 | 36 | /// Compare one instruction with its fixed wire representation. |
|
| 34 | 37 | unsafe fn instruction(item: il::Instr, expected: &[u8]) throws (testing::TestError) { |
|
| 35 | 38 | let mut buffer: [u8; 256] = [0; 256]; |
|
| 36 | - | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 37 | - | try writer::instr(&mut out, item) catch { |
|
| 38 | - | throw testing::TestError::Failed; |
|
| 39 | - | }; |
|
| 40 | - | try testing::expectBytesEq(&buffer[..out.offset], expected); |
|
| 41 | - | for capacity in 0..expected.len { |
|
| 42 | - | let mut short = writer::new(&mut buffer[..capacity], &["data", "fn"]); |
|
| 43 | - | let mut failed = false; |
|
| 44 | - | try writer::instr(&mut short, item) catch err { |
|
| 45 | - | try testing::expect(err == binary::Error::Capacity); |
|
| 46 | - | set failed = true; |
|
| 39 | + | let namesTable: [*[u8]; 2] = ["data", "fn"]; |
|
| 40 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 41 | + | let mut out = writer::new(storage, names); |
|
| 42 | + | try writer::instr(&mut out, item) catch { |
|
| 43 | + | throw testing::TestError::Failed; |
|
| 47 | 44 | }; |
|
| 48 | - | try testing::expect(failed); |
|
| 49 | - | try testing::expect(short.offset <= capacity); |
|
| 50 | - | } |
|
| 51 | - | let memory = &mut MEMORY[..512]; |
|
| 52 | - | let mut arena = alloc::new(&mut memory[..]); |
|
| 53 | - | let mut input = reader::new(expected, &mut arena, &["data", "fn"]); |
|
| 54 | - | set input.registers = 16; |
|
| 55 | - | set input.blocks = 4; |
|
| 56 | - | let decoded = try reader::instr(&mut input) catch { |
|
| 57 | - | throw testing::TestError::Failed; |
|
| 58 | - | }; |
|
| 59 | - | try testing::expect(input.offset == expected.len); |
|
| 60 | - | set out.offset = 0; |
|
| 61 | - | try writer::instr(&mut out, decoded) catch { |
|
| 62 | - | throw testing::TestError::Failed; |
|
| 63 | - | }; |
|
| 64 | - | try testing::expectBytesEq(&buffer[..out.offset], expected); |
|
| 65 | - | for length in 0..expected.len { |
|
| 66 | - | alloc::reset(&mut arena); |
|
| 67 | - | set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]); |
|
| 45 | + | try testing::expectBytesEq(&out.bytes[..out.offset], expected); |
|
| 46 | + | for capacity in 0..expected.len { |
|
| 47 | + | let shortStorage: 'short = &mut out.bytes[..capacity], shortNames = &out.symbols[..] in { |
|
| 48 | + | let mut short = writer::new(shortStorage, shortNames); |
|
| 49 | + | let mut failed = false; |
|
| 50 | + | try writer::instr(&mut short, item) catch err { |
|
| 51 | + | try testing::expect(err == binary::Error::Capacity); |
|
| 52 | + | set failed = true; |
|
| 53 | + | }; |
|
| 54 | + | try testing::expect(failed); |
|
| 55 | + | try testing::expect(short.offset <= capacity); |
|
| 56 | + | } |
|
| 57 | + | } |
|
| 58 | + | let memory = &mut MEMORY[..512]; |
|
| 59 | + | let mut arena = alloc::new(&mut memory[..]); |
|
| 60 | + | let mut input = reader::new(expected, &mut arena, &["data", "fn"]); |
|
| 68 | 61 | set input.registers = 16; |
|
| 69 | 62 | set input.blocks = 4; |
|
| 70 | - | let mut failed = false; |
|
| 71 | - | try reader::instr(&mut input) catch err { |
|
| 72 | - | try testing::expect(err == binary::Error::Truncated); |
|
| 73 | - | set failed = true; |
|
| 63 | + | let decoded = try reader::instr(&mut input) catch { |
|
| 64 | + | throw testing::TestError::Failed; |
|
| 74 | 65 | }; |
|
| 75 | - | try testing::expect(failed); |
|
| 76 | - | } |
|
| 66 | + | try testing::expect(input.offset == expected.len); |
|
| 67 | + | set out.offset = 0; |
|
| 68 | + | try writer::instr(&mut out, decoded) catch { |
|
| 69 | + | throw testing::TestError::Failed; |
|
| 70 | + | }; |
|
| 71 | + | try testing::expectBytesEq(&out.bytes[..out.offset], expected); |
|
| 72 | + | for length in 0..expected.len { |
|
| 73 | + | alloc::reset(&mut arena); |
|
| 74 | + | set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]); |
|
| 75 | + | set input.registers = 16; |
|
| 76 | + | set input.blocks = 4; |
|
| 77 | + | let mut failed = false; |
|
| 78 | + | try reader::instr(&mut input) catch err { |
|
| 79 | + | try testing::expect(err == binary::Error::Truncated); |
|
| 80 | + | set failed = true; |
|
| 81 | + | }; |
|
| 82 | + | try testing::expect(failed); |
|
| 83 | + | } |
|
| 77 | 84 | ||
| 85 | + | } |
|
| 78 | 86 | } |
|
| 79 | 87 | ||
| 80 | 88 | /// Check all instruction tags and their field order. |
|
| 81 | 89 | @test unsafe fn instructions() throws (testing::TestError) { |
|
| 82 | 90 | let mut args: [il::Val; 1] = [il::Val::Undef]; |
| 162 | 170 | } |
|
| 163 | 171 | ||
| 164 | 172 | /// Check every value tag and empty and nonempty sequences. |
|
| 165 | 173 | @test unsafe fn values() throws (testing::TestError) { |
|
| 166 | 174 | let mut buffer: [u8; 64] = [0; 64]; |
|
| 167 | - | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 168 | - | try writer::values(&mut out, &[]) catch { |
|
| 169 | - | throw testing::TestError::Failed; |
|
| 170 | - | }; |
|
| 171 | - | try writer::values(&mut out, &[ |
|
| 172 | - | il::Val::Reg(il::Reg { n: 0x12345678 }), |
|
| 173 | - | il::Val::Imm(-2), il::Val::DataSym("data"), |
|
| 174 | - | il::Val::FnAddr("fn"), il::Val::Undef, |
|
| 175 | - | ]) catch { |
|
| 176 | - | throw testing::TestError::Failed; |
|
| 177 | - | }; |
|
| 178 | - | try testing::expectBytesEq(&buffer[..out.offset], &[ |
|
| 179 | - | 0, 0, 0, 0, 5, 0, 0, 0, |
|
| 180 | - | 0, 0x78, 0x56, 0x34, 0x12, |
|
| 181 | - | 1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
|
| 182 | - | 2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4, |
|
| 183 | - | ]); |
|
| 184 | - | let memory = &mut MEMORY[..256]; |
|
| 185 | - | let mut arena = alloc::new(&mut memory[..]); |
|
| 186 | - | let mut input = reader::new(&buffer[..out.offset], &mut arena, &["data", "fn"]); |
|
| 187 | - | set input.registers = 0x12345679; |
|
| 188 | - | let empty = try reader::values(&mut input) catch { |
|
| 189 | - | throw testing::TestError::Failed; |
|
| 190 | - | }; |
|
| 191 | - | let decoded = try reader::values(&mut input) catch { |
|
| 192 | - | throw testing::TestError::Failed; |
|
| 193 | - | }; |
|
| 194 | - | try testing::expect(empty.len == 0); |
|
| 195 | - | try testing::expect(decoded.len == 5); |
|
| 196 | - | try testing::expect(input.offset == out.offset); |
|
| 197 | - | let mut repeated: [u8; 64] = [0; 64]; |
|
| 198 | - | let mut copy = writer::new(&mut repeated[..], &["data", "fn"]); |
|
| 199 | - | try writer::values(&mut copy, empty) catch { |
|
| 200 | - | throw testing::TestError::Failed; |
|
| 201 | - | }; |
|
| 202 | - | try writer::values(&mut copy, decoded) catch { |
|
| 203 | - | throw testing::TestError::Failed; |
|
| 204 | - | }; |
|
| 205 | - | try testing::expectBytesEq(&buffer[..out.offset], &repeated[..copy.offset]); |
|
| 206 | - | ||
| 207 | - | } |
|
| 208 | - | ||
| 209 | - | /// Check initializer bytes and repetition counts. |
|
| 210 | - | @test unsafe fn initializers() throws (testing::TestError) { |
|
| 211 | - | let mut buffer: [u8; 128] = [0; 128]; |
|
| 212 | - | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 213 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8, |
|
| 214 | - | val: -1 }, count: 0 }) catch { |
|
| 175 | + | let namesTable: [*[u8]; 2] = ["data", "fn"]; |
|
| 176 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 177 | + | let mut out = writer::new(storage, names); |
|
| 178 | + | try writer::values(&mut out, &[]) catch { |
|
| 215 | 179 | throw testing::TestError::Failed; |
|
| 216 | 180 | }; |
|
| 217 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16, |
|
| 218 | - | val: -2 }, count: 1 }) catch { |
|
| 181 | + | try writer::values(&mut out, &[ |
|
| 182 | + | il::Val::Reg(il::Reg { n: 0x12345678 }), |
|
| 183 | + | il::Val::Imm(-2), il::Val::DataSym("data"), |
|
| 184 | + | il::Val::FnAddr("fn"), il::Val::Undef, |
|
| 185 | + | ]) catch { |
|
| 219 | 186 | throw testing::TestError::Failed; |
|
| 220 | 187 | }; |
|
| 221 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32, |
|
| 222 | - | val: -3 }, count: 2 }) catch { |
|
| 188 | + | try testing::expectBytesEq(&out.bytes[..out.offset], &[ |
|
| 189 | + | 0, 0, 0, 0, 5, 0, 0, 0, |
|
| 190 | + | 0, 0x78, 0x56, 0x34, 0x12, |
|
| 191 | + | 1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
|
| 192 | + | 2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4, |
|
| 193 | + | ]); |
|
| 194 | + | let memory = &mut MEMORY[..256]; |
|
| 195 | + | let mut arena = alloc::new(&mut memory[..]); |
|
| 196 | + | let mut input = reader::new(&out.bytes[..out.offset], &mut arena, &["data", "fn"]); |
|
| 197 | + | set input.registers = 0x12345679; |
|
| 198 | + | let empty = try reader::values(&mut input) catch { |
|
| 223 | 199 | throw testing::TestError::Failed; |
|
| 224 | 200 | }; |
|
| 225 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, |
|
| 226 | - | val: -4 }, count: 3 }) catch { |
|
| 201 | + | let decoded = try reader::values(&mut input) catch { |
|
| 227 | 202 | throw testing::TestError::Failed; |
|
| 228 | 203 | }; |
|
| 229 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 }) |
|
| 230 | - | catch { |
|
| 231 | - | throw testing::TestError::Failed; |
|
| 232 | - | }; |
|
| 233 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch |
|
| 234 | - | { |
|
| 235 | - | throw testing::TestError::Failed; |
|
| 236 | - | }; |
|
| 237 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch |
|
| 238 | - | { |
|
| 239 | - | throw testing::TestError::Failed; |
|
| 240 | - | }; |
|
| 241 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch { |
|
| 242 | - | throw testing::TestError::Failed; }; |
|
| 243 | - | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch { |
|
| 244 | - | throw testing::TestError::Failed; }; |
|
| 245 | - | try testing::expectBytesEq(&buffer[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0, |
|
| 246 | - | 0, 0, 0, 4, 253, 255, 255, 255, 2, 0, 0, 0, 0, 8, 252, 255, 255, 255, 255, 255, 255, 255, 3, |
|
| 247 | - | 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 0, 0, 0, 5, 0, 0, 0, 3, 2, 0, 0, 0, 97, 98, 6, 0, |
|
| 248 | - | 0, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 4, 8, 0, 0, 0]); |
|
| 204 | + | try testing::expect(empty.len == 0); |
|
| 205 | + | try testing::expect(decoded.len == 5); |
|
| 206 | + | try testing::expect(input.offset == out.offset); |
|
| 207 | + | let mut repeated: [u8; 64] = [0; 64]; |
|
| 208 | + | let copyNamesTable: [*[u8]; 2] = ["data", "fn"]; |
|
| 209 | + | let copyStorage: 'copy = &mut repeated[..], copyNames = ©NamesTable[..] in { |
|
| 210 | + | let mut copy = writer::new(copyStorage, copyNames); |
|
| 211 | + | try writer::values(&mut copy, empty) catch { |
|
| 212 | + | throw testing::TestError::Failed; |
|
| 213 | + | }; |
|
| 214 | + | try writer::values(&mut copy, decoded) catch { |
|
| 215 | + | throw testing::TestError::Failed; |
|
| 216 | + | }; |
|
| 217 | + | try testing::expectBytesEq(&out.bytes[..out.offset], ©.bytes[..copy.offset]); |
|
| 218 | + | ||
| 219 | + | } |
|
| 220 | + | } |
|
| 221 | + | } |
|
| 222 | + | ||
| 223 | + | /// Check initializer bytes and repetition counts. |
|
| 224 | + | @test fn initializers() throws (testing::TestError) { |
|
| 225 | + | let mut buffer: [u8; 128] = [0; 128]; |
|
| 226 | + | let namesTable: [*[u8]; 2] = ["data", "fn"]; |
|
| 227 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 228 | + | let mut out = writer::new(storage, names); |
|
| 229 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8, |
|
| 230 | + | val: -1 }, count: 0 }) catch { |
|
| 231 | + | throw testing::TestError::Failed; |
|
| 232 | + | }; |
|
| 233 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16, |
|
| 234 | + | val: -2 }, count: 1 }) catch { |
|
| 235 | + | throw testing::TestError::Failed; |
|
| 236 | + | }; |
|
| 237 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32, |
|
| 238 | + | val: -3 }, count: 2 }) catch { |
|
| 239 | + | throw testing::TestError::Failed; |
|
| 240 | + | }; |
|
| 241 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, |
|
| 242 | + | val: -4 }, count: 3 }) catch { |
|
| 243 | + | throw testing::TestError::Failed; |
|
| 244 | + | }; |
|
| 245 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 }) |
|
| 246 | + | catch { |
|
| 247 | + | throw testing::TestError::Failed; |
|
| 248 | + | }; |
|
| 249 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch |
|
| 250 | + | { |
|
| 251 | + | throw testing::TestError::Failed; |
|
| 252 | + | }; |
|
| 253 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch |
|
| 254 | + | { |
|
| 255 | + | throw testing::TestError::Failed; |
|
| 256 | + | }; |
|
| 257 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch { |
|
| 258 | + | throw testing::TestError::Failed; }; |
|
| 259 | + | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch { |
|
| 260 | + | throw testing::TestError::Failed; }; |
|
| 261 | + | try testing::expectBytesEq(&out.bytes[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0, |
|
| 262 | + | 0, 0, 0, 4, 253, 255, 255, 255, 2, 0, 0, 0, 0, 8, 252, 255, 255, 255, 255, 255, 255, 255, 3, |
|
| 263 | + | 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 0, 0, 0, 5, 0, 0, 0, 3, 2, 0, 0, 0, 97, 98, 6, 0, |
|
| 264 | + | 0, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 4, 8, 0, 0, 0]); |
|
| 265 | + | } |
|
| 249 | 266 | } |
|
| 250 | 267 | ||
| 251 | 268 | /// Reject invalid widths, insufficient storage, and absent symbols. |
|
| 252 | - | @test unsafe fn errors() throws (testing::TestError) { |
|
| 269 | + | @test fn errors() throws (testing::TestError) { |
|
| 253 | 270 | let mut buffer: [u8; 8] = [0; 8]; |
|
| 254 | - | let mut out = writer::new(&mut buffer[..], &[]); |
|
| 255 | - | let mut failures: u32 = 0; |
|
| 256 | - | try writer::integer(&mut out, 1, 3) catch err { |
|
| 257 | - | try testing::expect(err == binary::Error::Invalid); |
|
| 258 | - | set failures += 1; |
|
| 259 | - | }; |
|
| 260 | - | try writer::symbol(&mut out, "absent") catch err { |
|
| 261 | - | try testing::expect(err == binary::Error::Symbol); |
|
| 262 | - | set failures += 1; |
|
| 263 | - | }; |
|
| 264 | - | try testing::expect(out.offset == 0); |
|
| 265 | - | try writer::integer(&mut out, 0, 8) catch { |
|
| 266 | - | throw testing::TestError::Failed; |
|
| 267 | - | }; |
|
| 268 | - | try writer::integer(&mut out, 1, 1) catch err { |
|
| 269 | - | try testing::expect(err == binary::Error::Capacity); |
|
| 270 | - | set failures += 1; |
|
| 271 | - | }; |
|
| 272 | - | try testing::expect(failures == 3); |
|
| 273 | - | try testing::expect(out.offset == 8); |
|
| 271 | + | let namesTable: [*[u8]; 0] = []; |
|
| 272 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 273 | + | let mut out = writer::new(storage, names); |
|
| 274 | + | let mut failures: u32 = 0; |
|
| 275 | + | try writer::integer(&mut out, 1, 3) catch err { |
|
| 276 | + | try testing::expect(err == binary::Error::Invalid); |
|
| 277 | + | set failures += 1; |
|
| 278 | + | }; |
|
| 279 | + | try writer::symbol(&mut out, "absent") catch err { |
|
| 280 | + | try testing::expect(err == binary::Error::Symbol); |
|
| 281 | + | set failures += 1; |
|
| 282 | + | }; |
|
| 283 | + | try testing::expect(out.offset == 0); |
|
| 284 | + | try writer::integer(&mut out, 0, 8) catch { |
|
| 285 | + | throw testing::TestError::Failed; |
|
| 286 | + | }; |
|
| 287 | + | try writer::integer(&mut out, 1, 1) catch err { |
|
| 288 | + | try testing::expect(err == binary::Error::Capacity); |
|
| 289 | + | set failures += 1; |
|
| 290 | + | }; |
|
| 291 | + | try testing::expect(failures == 3); |
|
| 292 | + | try testing::expect(out.offset == 8); |
|
| 293 | + | } |
|
| 274 | 294 | } |
|
| 275 | 295 | ||
| 276 | 296 | /// Check operation tags independently of the native union representation. |
|
| 277 | 297 | @test unsafe fn operations() throws (testing::TestError) { |
|
| 278 | 298 | let binaryOps = &[ |
| 305 | 325 | }, &[13, tag as u8, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]); |
|
| 306 | 326 | } |
|
| 307 | 327 | } |
|
| 308 | 328 | ||
| 309 | 329 | /// Check byte-sequence bounds at every output size. |
|
| 310 | - | @test unsafe fn byteCapacity() throws (testing::TestError) { |
|
| 330 | + | @test fn byteCapacity() throws (testing::TestError) { |
|
| 311 | 331 | let mut buffer: [u8; 7] = [0; 7]; |
|
| 312 | 332 | for capacity in 0..7 { |
|
| 313 | - | let mut out = writer::new(&mut buffer[..capacity], &[]); |
|
| 314 | - | let mut failed = false; |
|
| 315 | - | try writer::bytes(&mut out, "abc") catch err { |
|
| 316 | - | try testing::expect(err == binary::Error::Capacity); |
|
| 317 | - | set failed = true; |
|
| 333 | + | let namesTable: [*[u8]; 0] = []; |
|
| 334 | + | let storage: 'short = &mut buffer[..capacity], names = &namesTable[..] in { |
|
| 335 | + | let mut out = writer::new(storage, names); |
|
| 336 | + | let mut failed = false; |
|
| 337 | + | try writer::bytes(&mut out, "abc") catch err { |
|
| 338 | + | try testing::expect(err == binary::Error::Capacity); |
|
| 339 | + | set failed = true; |
|
| 340 | + | }; |
|
| 341 | + | try testing::expect(failed); |
|
| 342 | + | try testing::expect(out.offset <= capacity); |
|
| 343 | + | } |
|
| 344 | + | } |
|
| 345 | + | let namesTable: [*[u8]; 0] = []; |
|
| 346 | + | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
|
| 347 | + | let mut out = writer::new(storage, names); |
|
| 348 | + | try writer::bytes(&mut out, "abc") catch { |
|
| 349 | + | throw testing::TestError::Failed; |
|
| 318 | 350 | }; |
|
| 319 | - | try testing::expect(failed); |
|
| 320 | - | try testing::expect(out.offset <= capacity); |
|
| 351 | + | try testing::expectBytesEq(&out.bytes[..], &[3, 0, 0, 0, 97, 98, 99]); |
|
| 321 | 352 | } |
|
| 322 | - | let mut out = writer::new(&mut buffer[..], &[]); |
|
| 323 | - | try writer::bytes(&mut out, "abc") catch { |
|
| 324 | - | throw testing::TestError::Failed; |
|
| 325 | - | }; |
|
| 326 | - | try testing::expectBytesEq(&buffer[..], &[3, 0, 0, 0, 97, 98, 99]); |
|
| 327 | 353 | } |
lib/std/lang/il/binary/writer.rad
+19 -18
| 4 | 4 | use std::lang::il; |
|
| 5 | 5 | use std::mem; |
|
| 6 | 6 | use std::lang::il::binary; |
|
| 7 | 7 | ||
| 8 | 8 | /// Output cursor and the symbol table used by this package. |
|
| 9 | - | /// The output buffer and symbol table must remain valid until the last write. |
|
| 10 | - | export record Writer: Copy { |
|
| 9 | + | /// The buffer is exclusively borrowed for the writer's region. |
|
| 10 | + | export record Writer: 'buffer { |
|
| 11 | 11 | /// Destination bytes. |
|
| 12 | - | bytes: *unsafe mut [u8], |
|
| 12 | + | bytes: &'buffer mut [u8], |
|
| 13 | 13 | /// Number of bytes written. |
|
| 14 | 14 | offset: u32, |
|
| 15 | 15 | /// Names in wire-index order. The caller supplies unique names. |
|
| 16 | - | symbols: *unsafe [*[u8]], |
|
| 16 | + | symbols: &'buffer [*[u8]], |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | /// Create an output cursor without allocation. |
|
| 20 | - | /// The caller must retain exclusive access to the output buffer. |
|
| 21 | - | export unsafe fn new(bytes: &mut [u8], symbols: *unsafe [*[u8]]) -> Writer { |
|
| 22 | - | return Writer { bytes: bytes as *unsafe mut [u8], offset: 0, symbols }; |
|
| 20 | + | /// Retain the buffer and symbol table for the same region. |
|
| 21 | + | export fn new 'buffer (bytes: &'buffer mut [u8], symbols: &'buffer [*[u8]]) -> Writer 'buffer { |
|
| 22 | + | return Writer 'buffer { bytes, offset: 0, symbols }; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Write the low bytes of an integer with width 1, 2, 4, or 8. |
|
| 26 | - | export unsafe fn integer(out: &mut Writer, value: u64, width: u32) throws (binary::Error) { |
|
| 26 | + | export fn integer 'buffer (out: &mut Writer 'buffer, value: u64, width: u32) throws (binary::Error) { |
|
| 27 | 27 | if width <> 1 and width <> 2 and width <> 4 and width <> 8 { |
|
| 28 | 28 | throw binary::Error::Invalid; |
|
| 29 | 29 | } |
|
| 30 | 30 | if out.offset > out.bytes.len or width > out.bytes.len - out.offset { |
|
| 31 | 31 | throw binary::Error::Capacity; |
| 35 | 35 | } |
|
| 36 | 36 | set out.offset += width; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Write a byte sequence with its u32 length. |
|
| 40 | - | export unsafe fn bytes(out: &mut Writer, value: &[u8]) throws (binary::Error) { |
|
| 40 | + | export fn bytes 'buffer (out: &mut Writer 'buffer, value: &[u8]) throws (binary::Error) { |
|
| 41 | 41 | try integer(out, value.len as u64, 4); |
|
| 42 | 42 | if value.len > out.bytes.len - out.offset { |
|
| 43 | 43 | throw binary::Error::Capacity; |
|
| 44 | 44 | } |
|
| 45 | 45 | for b, i in value { |
| 47 | 47 | } |
|
| 48 | 48 | set out.offset += value.len; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Write the index of a name in the package symbol table. |
|
| 52 | - | export unsafe fn symbol(out: &mut Writer, name: &[u8]) throws (binary::Error) { |
|
| 52 | + | export fn symbol 'buffer (out: &mut Writer 'buffer, name: &[u8]) throws (binary::Error) { |
|
| 53 | 53 | for candidate, i in out.symbols { |
|
| 54 | 54 | if mem::eq(candidate, name) { |
|
| 55 | 55 | try integer(out, i as u64, 4); |
|
| 56 | 56 | return; |
|
| 57 | 57 | } |
|
| 58 | 58 | } |
|
| 59 | 59 | throw binary::Error::Symbol; |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | 62 | /// Write an IL type as its byte width. |
|
| 63 | - | export unsafe fn typ(out: &mut Writer, value: il::Type) throws (binary::Error) { |
|
| 63 | + | export fn typ 'buffer (out: &mut Writer 'buffer, value: il::Type) throws (binary::Error) { |
|
| 64 | 64 | try integer(out, il::typeSize(value) as u64, 1); |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Write a tagged value. Symbol addresses use table indices. |
|
| 68 | - | export unsafe fn val(out: &mut Writer, value: il::Val) throws (binary::Error) { |
|
| 68 | + | export fn val 'buffer (out: &mut Writer 'buffer, value: il::Val) throws (binary::Error) { |
|
| 69 | 69 | match value { |
|
| 70 | 70 | case il::Val::Reg(reg) => { |
|
| 71 | 71 | try integer(out, super::VALUE_REG as u64, 1); |
|
| 72 | 72 | try integer(out, reg.n as u64, 4); |
|
| 73 | 73 | }, |
| 86 | 86 | case il::Val::Undef => try integer(out, super::VALUE_UNDEF as u64, 1), |
|
| 87 | 87 | } |
|
| 88 | 88 | } |
|
| 89 | 89 | ||
| 90 | 90 | /// Write a sequence of values. |
|
| 91 | - | export unsafe fn values(out: &mut Writer, items: &[il::Val]) throws (binary::Error) { |
|
| 91 | + | export fn values 'buffer (out: &mut Writer 'buffer, items: &[il::Val]) throws (binary::Error) { |
|
| 92 | 92 | try integer(out, items.len as u64, 4); |
|
| 93 | 93 | for item in items { |
|
| 94 | 94 | try val(out, item); |
|
| 95 | 95 | } |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Write an explicit bin operation tag. |
|
| 99 | - | unsafe fn binOp(out: &mut Writer, value: il::BinOp) throws (binary::Error) { |
|
| 99 | + | fn binOp 'buffer (out: &mut Writer 'buffer, value: il::BinOp) throws (binary::Error) { |
|
| 100 | 100 | match value { |
|
| 101 | 101 | case il::BinOp::Add => try integer(out, super::BIN_ADD as u64, 1), |
|
| 102 | 102 | case il::BinOp::Sub => try integer(out, super::BIN_SUB as u64, 1), |
|
| 103 | 103 | case il::BinOp::Mul => try integer(out, super::BIN_MUL as u64, 1), |
|
| 104 | 104 | case il::BinOp::Sdiv => try integer(out, super::BIN_SDIV as u64, 1), |
| 119 | 119 | case il::BinOp::Ushr => try integer(out, super::BIN_USHR as u64, 1), |
|
| 120 | 120 | } |
|
| 121 | 121 | } |
|
| 122 | 122 | ||
| 123 | 123 | /// Write an explicit un operation tag. |
|
| 124 | - | unsafe fn unOp(out: &mut Writer, value: il::UnOp) throws (binary::Error) { |
|
| 124 | + | fn unOp 'buffer (out: &mut Writer 'buffer, value: il::UnOp) throws (binary::Error) { |
|
| 125 | 125 | match value { |
|
| 126 | 126 | case il::UnOp::Neg => try integer(out, super::UN_NEG as u64, 1), |
|
| 127 | 127 | case il::UnOp::Not => try integer(out, super::UN_NOT as u64, 1), |
|
| 128 | 128 | } |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | 131 | /// Write an explicit cmp operation tag. |
|
| 132 | - | unsafe fn cmpOp(out: &mut Writer, value: il::CmpOp) throws (binary::Error) { |
|
| 132 | + | fn cmpOp 'buffer (out: &mut Writer 'buffer, value: il::CmpOp) throws (binary::Error) { |
|
| 133 | 133 | match value { |
|
| 134 | 134 | case il::CmpOp::Eq => try integer(out, super::CMP_EQ as u64, 1), |
|
| 135 | 135 | case il::CmpOp::Ne => try integer(out, super::CMP_NE as u64, 1), |
|
| 136 | 136 | case il::CmpOp::Slt => try integer(out, super::CMP_SLT as u64, 1), |
|
| 137 | 137 | case il::CmpOp::Ult => try integer(out, super::CMP_ULT as u64, 1), |
|
| 138 | 138 | } |
|
| 139 | 139 | } |
|
| 140 | 140 | ||
| 141 | 141 | /// Write one instruction. Fields follow the IL record declaration order. |
|
| 142 | - | export unsafe fn instr(out: &mut Writer, item: il::Instr) throws (binary::Error) { |
|
| 142 | + | /// All raw instruction operand tables must remain valid during encoding. |
|
| 143 | + | export unsafe fn instr 'buffer (out: &mut Writer 'buffer, item: il::Instr) throws (binary::Error) { |
|
| 143 | 144 | match item { |
|
| 144 | 145 | case il::Instr::Reserve { dst: vdst, size: vsize, alignment: valignment } => { |
|
| 145 | 146 | try integer(out, super::INSTR_RESERVE as u64, 1); |
|
| 146 | 147 | try integer(out, vdst.n as u64, 4); |
|
| 147 | 148 | try val(out, vsize); |
| 286 | 287 | }, |
|
| 287 | 288 | } |
|
| 288 | 289 | } |
|
| 289 | 290 | ||
| 290 | 291 | /// Write one data initializer and its repetition count. |
|
| 291 | - | export unsafe fn dataValue(out: &mut Writer, value: il::DataValue) throws (binary::Error) { |
|
| 292 | + | export fn dataValue 'buffer (out: &mut Writer 'buffer, value: il::DataValue) throws (binary::Error) { |
|
| 292 | 293 | match value.item { |
|
| 293 | 294 | case il::DataItem::Val { typ: t, val: n } => { |
|
| 294 | 295 | try integer(out, super::DATA_VAL as u64, 1); |
|
| 295 | 296 | try typ(out, t); |
|
| 296 | 297 | try integer(out, n as u64, il::typeSize(t)); |