compiler: Decode fixed data items in checked code
8d09b8cba394fe010dd34d05ad9f6a87fc6d3d5317c6f32d05c033cb0e137e53
1 parent
603eae1b
lib/std/lang/il/binary/reader.rad
+12 -10
| 346 | 346 | } |
|
| 347 | 347 | ||
| 348 | 348 | /// Read an initializer with a repetition count. |
|
| 349 | 349 | export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) { |
|
| 350 | 350 | let tag = try integer(input, 1) as u8; |
|
| 351 | - | let mut item: il::DataItem = il::DataItem::Undef; |
|
| 351 | + | let item = il::DataItem::Str(try bytes(input)) if tag == super::DATA_STR |
|
| 352 | + | else try fixedDataItem(input, tag); |
|
| 353 | + | let n = try integer(input, 4) as u32; |
|
| 354 | + | return il::DataValue { item, count: n }; |
|
| 355 | + | } |
|
| 356 | + | ||
| 357 | + | /// Decode a data item whose payload needs no allocation. |
|
| 358 | + | fn fixedDataItem 'input (input: &mut Reader 'input, tag: u8) -> il::DataItem throws (binary::Error) { |
|
| 352 | 359 | match tag { |
|
| 353 | 360 | case super::DATA_VAL => { |
|
| 354 | 361 | let t = try typ(input); |
|
| 355 | 362 | let n = try integer(input, il::typeSize(t)); |
|
| 356 | - | set item = il::DataItem::Val { typ: t, val: n as i64 }; |
|
| 363 | + | return il::DataItem::Val { typ: t, val: n as i64 }; |
|
| 357 | 364 | }, |
|
| 358 | 365 | case super::DATA_SYM => { |
|
| 359 | - | set item = il::DataItem::Sym(try symbol(input)); |
|
| 366 | + | return il::DataItem::Sym(try symbol(input)); |
|
| 360 | 367 | }, |
|
| 361 | 368 | case super::DATA_FN => { |
|
| 362 | - | set item = il::DataItem::Fn(try symbol(input)); |
|
| 363 | - | }, |
|
| 364 | - | case super::DATA_STR => { |
|
| 365 | - | set item = il::DataItem::Str(try bytes(input)); |
|
| 369 | + | return il::DataItem::Fn(try symbol(input)); |
|
| 366 | 370 | }, |
|
| 367 | 371 | case super::DATA_UNDEF => { |
|
| 368 | - | set item = il::DataItem::Undef; |
|
| 372 | + | return il::DataItem::Undef; |
|
| 369 | 373 | }, |
|
| 370 | 374 | else => throw binary::Error::Invalid, |
|
| 371 | 375 | } |
|
| 372 | - | let n = try integer(input, 4) as u32; |
|
| 373 | - | return il::DataValue { item, count: n }; |
|
| 374 | 376 | } |
lib/std/lang/il/binary/tests.rad
+46 -0
| 10 | 10 | /// Decode arena backing storage. Tests reset it before each use. |
|
| 11 | 11 | static MEMORY: [u8; 512] = [0; 512]; |
|
| 12 | 12 | /// Marker for bytes beyond a writer's borrowed output extent. |
|
| 13 | 13 | constant OUTPUT_CANARY: u8 = 0xa5; |
|
| 14 | 14 | ||
| 15 | + | /// Fixed data items decode with no arena storage at every count boundary. |
|
| 16 | + | @test unsafe fn fixedDataItems() throws (testing::TestError) { |
|
| 17 | + | for item in [ |
|
| 18 | + | il::DataItem::Val { typ: il::Type::W8, val: 0x12 }, |
|
| 19 | + | il::DataItem::Val { typ: il::Type::W16, val: 0x1234 }, |
|
| 20 | + | il::DataItem::Val { typ: il::Type::W32, val: 0x12345678 }, |
|
| 21 | + | il::DataItem::Val { typ: il::Type::W64, val: -1 }, |
|
| 22 | + | il::DataItem::Sym("data"), il::DataItem::Fn("fn"), il::DataItem::Undef, |
|
| 23 | + | ] { |
|
| 24 | + | for count in [0 as u32, 1, 0xffffffff] { |
|
| 25 | + | try checkFixedDataItem(il::DataValue { item, count }); |
|
| 26 | + | } |
|
| 27 | + | } |
|
| 28 | + | } |
|
| 29 | + | ||
| 30 | + | /// Check a fixed initializer and every truncated prefix without allocation. |
|
| 31 | + | unsafe fn checkFixedDataItem(item: il::DataValue) throws (testing::TestError) { |
|
| 32 | + | let mut buffer: [u8; 32] = [0; 32]; |
|
| 33 | + | let symbols: [*[u8]; 2] = ["data", "fn"]; |
|
| 34 | + | let mut length: u32 = 0; |
|
| 35 | + | let output: 'output = &mut buffer[..], names = &symbols[..] in { |
|
| 36 | + | let mut out = writer::new(output, names); |
|
| 37 | + | try writer::dataValue(&mut out, item) catch { throw testing::TestError::Failed; }; |
|
| 38 | + | set length = out.offset; |
|
| 39 | + | } |
|
| 40 | + | for end in 0..length + 1 { |
|
| 41 | + | let mut arena = alloc::new(&mut MEMORY[..0]); |
|
| 42 | + | let source: 'input = &buffer[..end], names = &symbols[..] in { |
|
| 43 | + | let mut input = reader::new(source, &mut arena, names); |
|
| 44 | + | let mut failed = false; |
|
| 45 | + | let decoded = try reader::dataValue(&mut input) catch err { |
|
| 46 | + | assert err == binary::Error::Truncated; |
|
| 47 | + | set failed = true; |
|
| 48 | + | il::DataValue { item: il::DataItem::Undef, count: 0 } |
|
| 49 | + | }; |
|
| 50 | + | assert failed == (end < length); |
|
| 51 | + | assert input.offset <= end; |
|
| 52 | + | if not failed { |
|
| 53 | + | assert decoded == item; |
|
| 54 | + | assert input.offset == length; |
|
| 55 | + | } |
|
| 56 | + | } |
|
| 57 | + | assert arena.offset == 0; |
|
| 58 | + | } |
|
| 59 | + | } |
|
| 60 | + | ||
| 15 | 61 | /// Scalar decoding uses only checked input bytes and numeric cursor bounds. |
|
| 16 | 62 | fn checkScalarReader 'input (input: &mut reader::Reader 'input) throws (testing::TestError) { |
|
| 17 | 63 | set input.registers = 2; |
|
| 18 | 64 | set input.blocks = 3; |
|
| 19 | 65 | let reg = try reader::reg(input) catch { throw testing::TestError::Failed; }; |