compiler: Decode scalar RIL fields through regional input borrows
df6afe112faa7b92be060f31202dd5d56d7cea3234cfa6ef0c985863fb52f8a7
1 parent
c46d379a
lib/std/lang/il/binary/decodeTests.rad
+30 -24
| 52 | 52 | }; |
|
| 53 | 53 | try testing::expect(failed); |
|
| 54 | 54 | try testing::expect(arena.offset == 8); |
|
| 55 | 55 | } |
|
| 56 | 56 | set arena.offset = 8; |
|
| 57 | - | let mut input = reader::new(&[], &mut arena, &[]); |
|
| 58 | - | let mut exhausted = false; |
|
| 59 | - | try reader::storage(&mut input, 0xffffffff, 8, 0xffffffff) catch err { |
|
| 60 | - | try testing::expect(err == binary::Error::Storage); |
|
| 61 | - | set exhausted = true; |
|
| 62 | - | }; |
|
| 63 | - | try testing::expect(exhausted); |
|
| 64 | - | try testing::expect(arena.offset == 8); |
|
| 57 | + | let source: 'input = &buffer[..0] in { |
|
| 58 | + | let mut input = reader::new(source, &mut arena, &[]); |
|
| 59 | + | let mut exhausted = false; |
|
| 60 | + | try reader::storage(&mut input, 0xffffffff, 8, 0xffffffff) catch err { |
|
| 61 | + | try testing::expect(err == binary::Error::Storage); |
|
| 62 | + | set exhausted = true; |
|
| 63 | + | }; |
|
| 64 | + | try testing::expect(exhausted); |
|
| 65 | + | try testing::expect(arena.offset == 8); |
|
| 66 | + | } |
|
| 65 | 67 | // Exercise every smaller arena extent, including alignment boundaries. |
|
| 66 | 68 | let mut usedArena = alloc::new(&mut memory[..]); |
|
| 67 | 69 | let _ = try program::decode(&buffer[..length], &mut usedArena, LIMITS) catch { |
|
| 68 | 70 | throw testing::TestError::Failed; |
|
| 69 | 71 | }; |
| 149 | 151 | ||
| 150 | 152 | /// Reject an invalid encoded instruction without a panic. |
|
| 151 | 153 | unsafe fn badInstruction(bytes: *[u8], expected: binary::Error) throws (testing::TestError) { |
|
| 152 | 154 | let memory = &mut MEMORY[..64]; |
|
| 153 | 155 | let mut arena = alloc::new(&mut memory[..]); |
|
| 154 | - | let mut input = reader::new(bytes, &mut arena, &["p"]); |
|
| 155 | - | set input.registers = 2; |
|
| 156 | - | set input.blocks = 1; |
|
| 157 | - | let mut failed = false; |
|
| 158 | - | try reader::instr(&mut input) catch err { |
|
| 159 | - | try testing::expect(err == expected); |
|
| 160 | - | set failed = true; |
|
| 161 | - | }; |
|
| 162 | - | try testing::expect(failed); |
|
| 156 | + | let source: 'input = &bytes[..] in { |
|
| 157 | + | let mut input = reader::new(source, &mut arena, &["p"]); |
|
| 158 | + | set input.registers = 2; |
|
| 159 | + | set input.blocks = 1; |
|
| 160 | + | let mut failed = false; |
|
| 161 | + | try reader::instr(&mut input) catch err { |
|
| 162 | + | try testing::expect(err == expected); |
|
| 163 | + | set failed = true; |
|
| 164 | + | }; |
|
| 165 | + | try testing::expect(failed); |
|
| 166 | + | } |
|
| 163 | 167 | } |
|
| 164 | 168 | ||
| 165 | 169 | /// Check instruction tags, operand tags, types, optional flags, and indices. |
|
| 166 | 170 | @test unsafe fn malformedInstructions() throws (testing::TestError) { |
|
| 167 | 171 | try badInstruction(&[255], binary::Error::Invalid); |
| 293 | 297 | @test unsafe fn malformedInitializers() throws (testing::TestError) { |
|
| 294 | 298 | let memory = &mut MEMORY[..8]; |
|
| 295 | 299 | let mut arena = alloc::new(&mut memory[..]); |
|
| 296 | 300 | let fixtures: [*[u8]; 2] = [&[255], &[0, 3]]; |
|
| 297 | 301 | for bytes in &fixtures[..] { |
|
| 298 | - | let mut input = reader::new(bytes, &mut arena, &[]); |
|
| 299 | - | let mut failed = false; |
|
| 300 | - | try reader::dataValue(&mut input) catch err { |
|
| 301 | - | try testing::expect(err == binary::Error::Invalid); |
|
| 302 | - | set failed = true; |
|
| 303 | - | }; |
|
| 304 | - | try testing::expect(failed); |
|
| 302 | + | let source: 'input = &bytes[..] in { |
|
| 303 | + | let mut input = reader::new(source, &mut arena, &[]); |
|
| 304 | + | let mut failed = false; |
|
| 305 | + | try reader::dataValue(&mut input) catch err { |
|
| 306 | + | try testing::expect(err == binary::Error::Invalid); |
|
| 307 | + | set failed = true; |
|
| 308 | + | }; |
|
| 309 | + | try testing::expect(failed); |
|
| 310 | + | } |
|
| 305 | 311 | } |
|
| 306 | 312 | } |
|
| 307 | 313 | ||
| 308 | 314 | /// Check symbol collection capacity and package ownership failures. |
|
| 309 | 315 | @test unsafe fn collectionBounds() throws (testing::TestError) { |
lib/std/lang/il/binary/program.rad
+11 -9
| 84 | 84 | return out.offset; |
|
| 85 | 85 | } |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | 88 | /// Read typed SSA parameters with checked register indices. |
|
| 89 | - | unsafe fn readParams(input: &mut reader::Reader) -> *unsafe [il::Param] throws (binary::Error) { |
|
| 89 | + | unsafe fn readParams 'input (input: &mut reader::Reader 'input) -> *unsafe [il::Param] throws (binary::Error) { |
|
| 90 | 90 | let n = try reader::count(input, 5); |
|
| 91 | 91 | let params = try reader::storage(input, @sizeOf(il::Param), @alignOf(il::Param), n) |
|
| 92 | 92 | as *mut [il::Param]; |
|
| 93 | 93 | for i in 0..n { |
|
| 94 | 94 | let value = try reader::reg(input); |
| 97 | 97 | } |
|
| 98 | 98 | return (¶ms[..]) as *unsafe [il::Param]; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | 101 | /// Read global data and check initializer extents against declared storage. |
|
| 102 | - | unsafe fn readData(input: &mut reader::Reader) -> *[il::Data] throws (binary::Error) { |
|
| 102 | + | unsafe fn readData 'input (input: &mut reader::Reader 'input) -> *[il::Data] throws (binary::Error) { |
|
| 103 | 103 | let n = try reader::count(input, 18); |
|
| 104 | 104 | let items = try reader::storage(input, @sizeOf(il::Data), @alignOf(il::Data), n) |
|
| 105 | 105 | as *mut [il::Data]; |
|
| 106 | 106 | for i in 0..n { |
|
| 107 | 107 | let name = try reader::symbol(input); |
| 143 | 143 | } |
|
| 144 | 144 | return items; |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Read functions with checked block and register indices. |
|
| 148 | - | unsafe fn readFunctions(input: &mut reader::Reader, limits: binary::Limits) |
|
| 148 | + | unsafe fn readFunctions 'input (input: &mut reader::Reader 'input, limits: binary::Limits) |
|
| 149 | 149 | -> *unsafe [*unsafe il::Fn] throws (binary::Error) |
|
| 150 | 150 | { |
|
| 151 | 151 | let n = try reader::count(input, 14); |
|
| 152 | 152 | let fns = try reader::storage(input, @sizeOf(*il::Fn), @alignOf(*il::Fn), n) as *mut [*unsafe il::Fn]; |
|
| 153 | 153 | set input.registers = limits.registers; |
| 193 | 193 | } |
|
| 194 | 194 | return (&fns[..]) as *unsafe [*unsafe il::Fn]; |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Read the package tables. The input must contain exactly one package. |
|
| 198 | - | unsafe fn readPackage(input: &mut reader::Reader, limits: binary::Limits) |
|
| 198 | + | unsafe fn readPackage 'input (input: &mut reader::Reader 'input, limits: binary::Limits) |
|
| 199 | 199 | -> binary::Package throws (binary::Error) |
|
| 200 | 200 | { |
|
| 201 | 201 | let magic = try reader::integer(input, 4) as u32; |
|
| 202 | 202 | let version = try reader::integer(input, 4) as u32; |
|
| 203 | 203 | if magic <> binary::MAGIC or version <> binary::VERSION { |
| 250 | 250 | /// The caller must retain the decoded allocations until the last package use. |
|
| 251 | 251 | export unsafe fn decode(bytes: &[u8], arena: &mut alloc::Arena, limits: binary::Limits) |
|
| 252 | 252 | -> binary::Package throws (binary::Error) |
|
| 253 | 253 | { |
|
| 254 | 254 | let saved = alloc::save(arena); |
|
| 255 | - | let mut input = reader::new(bytes, arena, &[]); |
|
| 256 | - | return try readPackage(&mut input, limits) catch err { |
|
| 257 | - | alloc::restore(arena, saved); |
|
| 258 | - | throw err; |
|
| 259 | - | }; |
|
| 255 | + | let source: 'input = &bytes[..] in { |
|
| 256 | + | let mut input = reader::new(source, &mut *arena, &[]); |
|
| 257 | + | return try readPackage(&mut input, limits) catch err { |
|
| 258 | + | alloc::restore(arena, saved); |
|
| 259 | + | throw err; |
|
| 260 | + | }; |
|
| 261 | + | } |
|
| 260 | 262 | } |
lib/std/lang/il/binary/reader.rad
+22 -21
| 4 | 4 | use std::lang::il; |
|
| 5 | 5 | use std::lang::il::binary; |
|
| 6 | 6 | use std::lang::alloc; |
|
| 7 | 7 | ||
| 8 | 8 | /// Input cursor and reconstruction storage. |
|
| 9 | - | /// Input bytes, symbols, and the arena must remain valid during each read. |
|
| 10 | - | export record Reader: Copy { |
|
| 9 | + | /// Input bytes are borrowed for the reader's region. Allocation and symbol |
|
| 10 | + | /// access require valid raw storage. |
|
| 11 | + | export record Reader: 'input + Copy { |
|
| 11 | 12 | /// Encoded bytes. |
|
| 12 | - | bytes: *unsafe [u8], |
|
| 13 | + | bytes: &'input [u8], |
|
| 13 | 14 | /// Number of bytes consumed. |
|
| 14 | 15 | offset: u32, |
|
| 15 | 16 | /// Storage for decoded sequences and byte strings. |
|
| 16 | 17 | arena: *unsafe mut alloc::Arena, |
|
| 17 | 18 | /// Symbol names in wire-index order. |
| 21 | 22 | /// Number of blocks in the current function. |
|
| 22 | 23 | blocks: u32, |
|
| 23 | 24 | } |
|
| 24 | 25 | ||
| 25 | 26 | /// Create a cursor. Set function bounds before reading instructions. |
|
| 26 | - | export unsafe fn new(bytes: &[u8], arena: &mut alloc::Arena, symbols: *unsafe [*[u8]]) -> Reader { |
|
| 27 | - | return Reader { bytes: bytes as *unsafe [u8], offset: 0, arena: arena as *unsafe mut alloc::Arena, symbols, registers: 0, blocks: 0 }; |
|
| 27 | + | export fn new 'input (bytes: &'input [u8], arena: *unsafe mut alloc::Arena, symbols: *unsafe [*[u8]]) -> Reader 'input { |
|
| 28 | + | return Reader 'input { bytes, offset: 0, arena, symbols, registers: 0, blocks: 0 }; |
|
| 28 | 29 | } |
|
| 29 | 30 | ||
| 30 | 31 | /// Read an unsigned integer with width 1, 2, 4, or 8. |
|
| 31 | - | export unsafe fn integer(input: &mut Reader, width: u32) -> u64 throws (binary::Error) { |
|
| 32 | + | export fn integer 'input (input: &mut Reader 'input, width: u32) -> u64 throws (binary::Error) { |
|
| 32 | 33 | if width <> 1 and width <> 2 and width <> 4 and width <> 8 { |
|
| 33 | 34 | throw binary::Error::Invalid; |
|
| 34 | 35 | } |
|
| 35 | 36 | if input.offset > input.bytes.len or width > input.bytes.len - input.offset { |
|
| 36 | 37 | throw binary::Error::Truncated; |
| 42 | 43 | set input.offset += width; |
|
| 43 | 44 | return result; |
|
| 44 | 45 | } |
|
| 45 | 46 | ||
| 46 | 47 | /// Read a zero-or-one optional-field or boolean marker. |
|
| 47 | - | export unsafe fn flag(input: &mut Reader) -> bool throws (binary::Error) { |
|
| 48 | + | export fn flag 'input (input: &mut Reader 'input) -> bool throws (binary::Error) { |
|
| 48 | 49 | let n = try integer(input, 1); |
|
| 49 | 50 | if n > 1 { |
|
| 50 | 51 | throw binary::Error::Invalid; |
|
| 51 | 52 | } |
|
| 52 | 53 | return n == 1; |
|
| 53 | 54 | } |
|
| 54 | 55 | ||
| 55 | 56 | /// Read a count and check the minimum required input bytes before allocation. |
|
| 56 | - | export unsafe fn count(input: &mut Reader, minimum: u32) -> u32 throws (binary::Error) { |
|
| 57 | + | export fn count 'input (input: &mut Reader 'input, minimum: u32) -> u32 throws (binary::Error) { |
|
| 57 | 58 | assert minimum > 0; |
|
| 58 | 59 | let n = try integer(input, 4) as u32; |
|
| 59 | 60 | if n > (input.bytes.len - input.offset) / minimum { |
|
| 60 | 61 | throw binary::Error::Truncated; |
|
| 61 | 62 | } |
|
| 62 | 63 | return n; |
|
| 63 | 64 | } |
|
| 64 | 65 | ||
| 65 | 66 | /// Allocate a typed sequence after checking size and alignment arithmetic. |
|
| 66 | - | export unsafe fn storage(input: &mut Reader, size: u32, alignment: u32, count: u32) |
|
| 67 | + | export unsafe fn storage 'input (input: &mut Reader 'input, size: u32, alignment: u32, count: u32) |
|
| 67 | 68 | -> *mut [opaque] throws (binary::Error) |
|
| 68 | 69 | { |
|
| 69 | 70 | assert size > 0 and alignment > 0 and (alignment & (alignment - 1)) == 0; |
|
| 70 | 71 | return try alloc::allocSlice(input.arena, size, alignment, count) catch { |
|
| 71 | 72 | throw binary::Error::Storage; |
|
| 72 | 73 | }; |
|
| 73 | 74 | } |
|
| 74 | 75 | ||
| 75 | 76 | /// Read a length-prefixed byte string and copy it into the arena. |
|
| 76 | - | export unsafe fn bytes(input: &mut Reader) -> *[u8] throws (binary::Error) { |
|
| 77 | + | export unsafe fn bytes 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) { |
|
| 77 | 78 | let n = try count(input, 1); |
|
| 78 | 79 | let result = try storage(input, @sizeOf(u8), @alignOf(u8), n) as *mut [u8]; |
|
| 79 | 80 | for i in 0..n { |
|
| 80 | 81 | set result[i] = input.bytes[input.offset + i]; |
|
| 81 | 82 | } |
|
| 82 | 83 | set input.offset += n; |
|
| 83 | 84 | return result; |
|
| 84 | 85 | } |
|
| 85 | 86 | ||
| 86 | 87 | /// Resolve a checked symbol-table index. |
|
| 87 | - | export unsafe fn symbol(input: &mut Reader) -> *[u8] throws (binary::Error) { |
|
| 88 | + | export unsafe fn symbol 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) { |
|
| 88 | 89 | let index = try integer(input, 4) as u32; |
|
| 89 | 90 | if index >= input.symbols.len { |
|
| 90 | 91 | throw binary::Error::Symbol; |
|
| 91 | 92 | } |
|
| 92 | 93 | return input.symbols[index]; |
|
| 93 | 94 | } |
|
| 94 | 95 | ||
| 95 | 96 | /// Read an IL type from its byte width. |
|
| 96 | - | export unsafe fn typ(input: &mut Reader) -> il::Type throws (binary::Error) { |
|
| 97 | + | export fn typ 'input (input: &mut Reader 'input) -> il::Type throws (binary::Error) { |
|
| 97 | 98 | let width = try integer(input, 1); |
|
| 98 | 99 | match width { |
|
| 99 | 100 | case 1 => return il::Type::W8, |
|
| 100 | 101 | case 2 => return il::Type::W16, |
|
| 101 | 102 | case 4 => return il::Type::W32, |
| 103 | 104 | else => throw binary::Error::Invalid, |
|
| 104 | 105 | } |
|
| 105 | 106 | } |
|
| 106 | 107 | ||
| 107 | 108 | /// Read a register index within the current function's bound. |
|
| 108 | - | export unsafe fn reg(input: &mut Reader) -> il::Reg throws (binary::Error) { |
|
| 109 | + | export fn reg 'input (input: &mut Reader 'input) -> il::Reg throws (binary::Error) { |
|
| 109 | 110 | let n = try integer(input, 4) as u32; |
|
| 110 | 111 | if n >= input.registers { |
|
| 111 | 112 | throw binary::Error::Invalid; |
|
| 112 | 113 | } |
|
| 113 | 114 | return il::Reg { n }; |
|
| 114 | 115 | } |
|
| 115 | 116 | ||
| 116 | 117 | /// Read a target block index within the current function. |
|
| 117 | - | export unsafe fn target(input: &mut Reader) -> u32 throws (binary::Error) { |
|
| 118 | + | export fn target 'input (input: &mut Reader 'input) -> u32 throws (binary::Error) { |
|
| 118 | 119 | let n = try integer(input, 4) as u32; |
|
| 119 | 120 | if n >= input.blocks { |
|
| 120 | 121 | throw binary::Error::Invalid; |
|
| 121 | 122 | } |
|
| 122 | 123 | return n; |
|
| 123 | 124 | } |
|
| 124 | 125 | ||
| 125 | 126 | /// Read a tagged value with checked register and symbol indices. |
|
| 126 | - | export unsafe fn val(input: &mut Reader) -> il::Val throws (binary::Error) { |
|
| 127 | + | export unsafe fn val 'input (input: &mut Reader 'input) -> il::Val throws (binary::Error) { |
|
| 127 | 128 | let tag = try integer(input, 1) as u8; |
|
| 128 | 129 | match tag { |
|
| 129 | 130 | case super::VALUE_REG => return il::Val::Reg(try reg(input)), |
|
| 130 | 131 | case super::VALUE_IMM => return il::Val::Imm(try integer(input, 8) as i64), |
|
| 131 | 132 | case super::VALUE_DATASYM => return il::Val::DataSym(try symbol(input)), |
| 134 | 135 | else => throw binary::Error::Invalid, |
|
| 135 | 136 | } |
|
| 136 | 137 | } |
|
| 137 | 138 | ||
| 138 | 139 | /// Read a counted sequence of values. |
|
| 139 | - | export unsafe fn values(input: &mut Reader) -> *unsafe mut [il::Val] throws (binary::Error) { |
|
| 140 | + | export unsafe fn values 'input (input: &mut Reader 'input) -> *unsafe mut [il::Val] throws (binary::Error) { |
|
| 140 | 141 | let n = try count(input, 1); |
|
| 141 | 142 | let result = try storage(input, @sizeOf(il::Val), @alignOf(il::Val), n) as *mut [il::Val]; |
|
| 142 | 143 | for i in 0..n { |
|
| 143 | 144 | set result[i] = try val(input); |
|
| 144 | 145 | } |
|
| 145 | 146 | return (&mut result[..]) as *unsafe mut [il::Val]; |
|
| 146 | 147 | } |
|
| 147 | 148 | ||
| 148 | 149 | /// Read a checked bin operation tag. |
|
| 149 | - | unsafe fn binOp(input: &mut Reader) -> il::BinOp throws (binary::Error) { |
|
| 150 | + | fn binOp 'input (input: &mut Reader 'input) -> il::BinOp throws (binary::Error) { |
|
| 150 | 151 | let tag = try integer(input, 1) as u8; |
|
| 151 | 152 | match tag { |
|
| 152 | 153 | case super::BIN_ADD => return il::BinOp::Add, |
|
| 153 | 154 | case super::BIN_SUB => return il::BinOp::Sub, |
|
| 154 | 155 | case super::BIN_MUL => return il::BinOp::Mul, |
| 171 | 172 | else => throw binary::Error::Invalid, |
|
| 172 | 173 | } |
|
| 173 | 174 | } |
|
| 174 | 175 | ||
| 175 | 176 | /// Read a checked un operation tag. |
|
| 176 | - | unsafe fn unOp(input: &mut Reader) -> il::UnOp throws (binary::Error) { |
|
| 177 | + | fn unOp 'input (input: &mut Reader 'input) -> il::UnOp throws (binary::Error) { |
|
| 177 | 178 | let tag = try integer(input, 1) as u8; |
|
| 178 | 179 | match tag { |
|
| 179 | 180 | case super::UN_NEG => return il::UnOp::Neg, |
|
| 180 | 181 | case super::UN_NOT => return il::UnOp::Not, |
|
| 181 | 182 | else => throw binary::Error::Invalid, |
|
| 182 | 183 | } |
|
| 183 | 184 | } |
|
| 184 | 185 | ||
| 185 | 186 | /// Read a checked cmp operation tag. |
|
| 186 | - | unsafe fn cmpOp(input: &mut Reader) -> il::CmpOp throws (binary::Error) { |
|
| 187 | + | fn cmpOp 'input (input: &mut Reader 'input) -> il::CmpOp throws (binary::Error) { |
|
| 187 | 188 | let tag = try integer(input, 1) as u8; |
|
| 188 | 189 | match tag { |
|
| 189 | 190 | case super::CMP_EQ => return il::CmpOp::Eq, |
|
| 190 | 191 | case super::CMP_NE => return il::CmpOp::Ne, |
|
| 191 | 192 | case super::CMP_SLT => return il::CmpOp::Slt, |
| 193 | 194 | else => throw binary::Error::Invalid, |
|
| 194 | 195 | } |
|
| 195 | 196 | } |
|
| 196 | 197 | ||
| 197 | 198 | /// Read one instruction and reconstruct its operand sequences. |
|
| 198 | - | export unsafe fn instr(input: &mut Reader) -> il::Instr throws (binary::Error) { |
|
| 199 | + | export unsafe fn instr 'input (input: &mut Reader 'input) -> il::Instr throws (binary::Error) { |
|
| 199 | 200 | let tag = try integer(input, 1) as u8; |
|
| 200 | 201 | match tag { |
|
| 201 | 202 | case super::INSTR_RESERVE => { |
|
| 202 | 203 | let vdst = try reg(input); |
|
| 203 | 204 | let vsize = try val(input); |
| 336 | 337 | else => throw binary::Error::Invalid, |
|
| 337 | 338 | } |
|
| 338 | 339 | } |
|
| 339 | 340 | ||
| 340 | 341 | /// Read an initializer with a repetition count. |
|
| 341 | - | export unsafe fn dataValue(input: &mut Reader) -> il::DataValue throws (binary::Error) { |
|
| 342 | + | export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) { |
|
| 342 | 343 | let tag = try integer(input, 1) as u8; |
|
| 343 | 344 | let mut item: il::DataItem = il::DataItem::Undef; |
|
| 344 | 345 | match tag { |
|
| 345 | 346 | case super::DATA_VAL => { |
|
| 346 | 347 | let t = try typ(input); |
lib/std/lang/il/binary/tests.rad
+61 -20
| 8 | 8 | use std::lang::alloc; |
|
| 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 | + | /// Scalar decoding uses only checked input bytes and numeric cursor bounds. |
|
| 14 | + | fn checkScalarReader 'input (input: &mut reader::Reader 'input) throws (testing::TestError) { |
|
| 15 | + | set input.registers = 2; |
|
| 16 | + | set input.blocks = 3; |
|
| 17 | + | let reg = try reader::reg(input) catch { throw testing::TestError::Failed; }; |
|
| 18 | + | try testing::expect(reg.n == 1); |
|
| 19 | + | let target = try reader::target(input) catch { throw testing::TestError::Failed; }; |
|
| 20 | + | try testing::expect(target == 2); |
|
| 21 | + | let mut truncated = false; |
|
| 22 | + | try reader::integer(input, 1) catch err { |
|
| 23 | + | try testing::expect(err == binary::Error::Truncated); |
|
| 24 | + | set truncated = true; |
|
| 25 | + | }; |
|
| 26 | + | try testing::expect(truncated); |
|
| 27 | + | set input.offset = 0; |
|
| 28 | + | let flag = try reader::flag(input) catch { throw testing::TestError::Failed; }; |
|
| 29 | + | try testing::expect(flag); |
|
| 30 | + | set input.offset = 0; |
|
| 31 | + | let typ = try reader::typ(input) catch { throw testing::TestError::Failed; }; |
|
| 32 | + | try testing::expect(typ == il::Type::W8); |
|
| 33 | + | set input.offset = 0; |
|
| 34 | + | let count = try reader::count(input, 4) catch { throw testing::TestError::Failed; }; |
|
| 35 | + | try testing::expect(count == 1); |
|
| 36 | + | set input.offset = 0; |
|
| 37 | + | let wide = try reader::integer(input, 8) catch { throw testing::TestError::Failed; }; |
|
| 38 | + | try testing::expect(wide == 0x0000000200000001); |
|
| 39 | + | } |
|
| 40 | + | ||
| 41 | + | /// A regional input buffer supports safe scalar decoding. |
|
| 42 | + | @test unsafe fn scalarReader() throws (testing::TestError) { |
|
| 43 | + | let memory = &mut MEMORY[..]; |
|
| 44 | + | let mut arena = alloc::new(memory); |
|
| 45 | + | let bytes: [u8; 8] = [1, 0, 0, 0, 2, 0, 0, 0]; |
|
| 46 | + | let source: 'input = &bytes[..] in { |
|
| 47 | + | let mut input = reader::new(source, &mut arena, &[]); |
|
| 48 | + | try checkScalarReader(&mut input); |
|
| 49 | + | } |
|
| 50 | + | } |
|
| 51 | + | ||
| 13 | 52 | /// Check little-endian encoding for every integer width. |
|
| 14 | 53 | @test fn integers() throws (testing::TestError) { |
|
| 15 | 54 | let mut buffer: [u8; 15] = [0; 15]; |
|
| 16 | 55 | let namesTable: [*[u8]; 0] = []; |
|
| 17 | 56 | let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in { |
| 55 | 94 | try testing::expect(short.offset <= capacity); |
|
| 56 | 95 | } |
|
| 57 | 96 | } |
|
| 58 | 97 | let memory = &mut MEMORY[..512]; |
|
| 59 | 98 | let mut arena = alloc::new(&mut memory[..]); |
|
| 60 | - | let mut input = reader::new(expected, &mut arena, &["data", "fn"]); |
|
| 61 | - | set input.registers = 16; |
|
| 62 | - | set input.blocks = 4; |
|
| 63 | - | let decoded = try reader::instr(&mut input) catch { |
|
| 64 | - | throw testing::TestError::Failed; |
|
| 65 | - | }; |
|
| 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"]); |
|
| 99 | + | let source: 'input = &expected[..] in { |
|
| 100 | + | let mut input = reader::new(source, &mut arena, &["data", "fn"]); |
|
| 75 | 101 | set input.registers = 16; |
|
| 76 | 102 | 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; |
|
| 103 | + | let decoded = try reader::instr(&mut input) catch { |
|
| 104 | + | throw testing::TestError::Failed; |
|
| 81 | 105 | }; |
|
| 82 | - | try testing::expect(failed); |
|
| 106 | + | try testing::expect(input.offset == expected.len); |
|
| 107 | + | set out.offset = 0; |
|
| 108 | + | try writer::instr(&mut out, decoded) catch { |
|
| 109 | + | throw testing::TestError::Failed; |
|
| 110 | + | }; |
|
| 111 | + | try testing::expectBytesEq(&out.bytes[..out.offset], expected); |
|
| 112 | + | for length in 0..expected.len { |
|
| 113 | + | alloc::reset(&mut arena); |
|
| 114 | + | set input = reader::new(&source[..length], &mut arena, &["data", "fn"]); |
|
| 115 | + | set input.registers = 16; |
|
| 116 | + | set input.blocks = 4; |
|
| 117 | + | let mut failed = false; |
|
| 118 | + | try reader::instr(&mut input) catch err { |
|
| 119 | + | try testing::expect(err == binary::Error::Truncated); |
|
| 120 | + | set failed = true; |
|
| 121 | + | }; |
|
| 122 | + | try testing::expect(failed); |
|
| 123 | + | } |
|
| 83 | 124 | } |
|
| 84 | 125 | ||
| 85 | 126 | } |
|
| 86 | 127 | } |
|
| 87 | 128 |