1#![expect(incomplete_features, reason = "explicit_tail_calls")]
31#![feature(
32 const_block_items,
33 const_cmp,
34 const_convert,
35 const_default,
36 const_index,
37 const_trait_impl,
38 const_try,
39 const_try_residual,
40 core_io,
41 core_io_borrowed_buf,
42 derive_const,
43 explicit_tail_calls,
44 fn_align,
45 maybe_uninit_fill,
46 signed_bigint_helpers,
47 trusted_len,
48 try_blocks
49)]
50#![no_std]
51
52pub mod instruction;
53
54use crate::instruction::ContractInstruction;
55use ab_contracts_common::metadata::decode::{
56 MetadataDecoder, MetadataDecodingError, MetadataItem, MethodMetadataItem,
57 MethodsMetadataDecoder,
58};
59use ab_io_type::trivial_type::TrivialType;
60use ab_io_type::unaligned::Unaligned;
61use ab_riscv_primitives::prelude::*;
62use core::io::BorrowedCursor;
63use core::iter;
64use core::iter::TrustedLen;
65use replace_with::replace_with_or_abort;
66use tracing::{debug, trace};
67
68pub const CONTRACT_FILE_MAGIC: [u8; 4] = *b"ABC0";
70
71const {
73 assert!(size_of::<ContractInstruction>() == 8);
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, TrivialType)]
78#[repr(C)]
79pub struct ContractFileHeader {
80 pub magic: [u8; 4],
82 pub read_only_section_file_size: u32,
84 pub read_only_section_memory_size: u32,
88 pub metadata_offset: u32,
90 pub metadata_size: u16,
92 pub num_methods: u16,
94 pub host_call_fn_offset: u32,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, TrivialType)]
102#[repr(C)]
103pub struct ContractFileMethodMetadata {
104 pub offset: u32,
106 pub size: u32,
108}
109
110#[derive(Debug, Copy, Clone)]
111pub struct ContractFileMethod<'a> {
112 pub address: u32,
114 pub size: u32,
116 pub method_metadata_item: MethodMetadataItem<'a>,
118 pub method_metadata_bytes: &'a [u8],
124}
125
126#[derive(Debug, thiserror::Error)]
128pub enum ContractFileParseError {
129 #[error("The file is too large, must fit into `u32`: {file_size} bytes")]
131 FileTooLarge {
132 file_size: usize,
134 },
135 #[error("The file does not have a header (not enough bytes)")]
137 NoHeader,
138 #[error("The magic bytes in the header are incorrect")]
140 WrongMagicBytes,
141 #[error(
143 "The metadata section is out of bounds of the file: offset {offset}, size {size}, file \
144 size {file_size}"
145 )]
146 MetadataOutOfRange {
147 offset: u32,
149 size: u16,
151 file_size: u32,
153 },
154 #[error("Failed to decode metadata item")]
156 MetadataDecoding,
157 #[error(
159 "The file is too small: num_methods {num_methods}, read_only_section_size \
160 {read_only_section_size}, file_size {file_size}"
161 )]
162 FileTooSmall {
163 num_methods: u16,
165 read_only_section_size: u32,
167 file_size: u32,
169 },
170 #[error("Method is unaligned: file offset {file_offset}, memory address {memory_address}")]
172 MethodUnaligned {
173 file_offset: u32,
175 memory_address: u32,
177 },
178 #[error(
180 "Method offset is out of bounds of the file: offset {offset}, code section \
181 offset {code_section_offset} file_size {file_size}"
182 )]
183 MethodOutOfRange {
184 offset: u32,
186 code_section_offset: u32,
188 file_size: u32,
190 },
191 #[error(
193 "Host call function is unaligned: file offset {file_offset}, memory address \
194 {memory_address}"
195 )]
196 HostCallFnUnaligned {
197 file_offset: u32,
199 memory_address: u32,
201 },
202 #[error(
204 "The host call function offset is out of bounds of the file: offset {offset}, code section \
205 offset {code_section_offset} file_size {file_size}"
206 )]
207 HostCallFnOutOfRange {
208 offset: u32,
210 code_section_offset: u32,
212 file_size: u32,
214 },
215 #[error("The host call function doesn't have jal tailcall instruction: {instruction}")]
217 InvalidHostCallFnPattern {
218 instruction: ContractInstruction,
220 },
221 #[error(
223 "The read-only section file size is larger than the memory size: file_size {file_size}, \
224 memory_size {memory_size}"
225 )]
226 InvalidReadOnlySizes {
227 file_size: u32,
229 memory_size: u32,
231 },
232 #[error(
234 "Contract memory size doesn't fit into `u32`: read_only_section_memory_size \
235 {read_only_section_memory_size}, code_size {code_size}"
236 )]
237 ContractMemoryTooLarge {
238 read_only_section_memory_size: u32,
240 code_size: u32,
242 },
243 #[error(
246 "There are not enough methods in the header to match the number of methods in the actual \
247 metadata: header_num_methods {header_num_methods}, metadata_method_index \
248 {metadata_method_index}"
249 )]
250 InsufficientHeaderMethods {
251 header_num_methods: u16,
253 metadata_method_index: u16,
255 },
256 #[error(
259 "The number of methods in the header {header_num_methods} does not match the number of \
260 methods in the actual metadata {metadata_num_methods}"
261 )]
262 MetadataNumMethodsMismatch {
263 header_num_methods: u16,
265 metadata_num_methods: u16,
267 },
268 #[error("Invalid instruction encountered while parsing the code section: {instruction:#x}")]
270 InvalidInstruction {
271 instruction: u32,
273 },
274 #[error("The code section is empty")]
276 CodeEmpty,
277 #[error(
279 "Unexpected trailing code bytes encountered while parsing the code section: {num_bytes} \
280 trailing bytes"
281 )]
282 UnexpectedTrailingCodeBytes {
283 num_bytes: usize,
285 },
286}
287
288impl From<MetadataDecodingError<'_>> for ContractFileParseError {
289 fn from(error: MetadataDecodingError<'_>) -> Self {
290 debug!(?error, "Failed to decode metadata item");
291 Self::MetadataDecoding
292 }
293}
294
295#[derive(Debug)]
297pub struct ContractFile<'a> {
298 read_only_section_file_size: u32,
299 read_only_section_memory_size: u32,
300 num_methods: u16,
301 bytes: &'a [u8],
302}
303
304impl<'a> ContractFile<'a> {
305 pub fn parse<CM>(
315 file_bytes: &'a [u8],
316 mut contract_method: CM,
317 ) -> Result<Self, ContractFileParseError>
318 where
319 CM: FnMut(ContractFileMethod<'a>) -> Result<(), ContractFileParseError>,
320 {
321 let file_size = u32::try_from(file_bytes.len()).map_err(|_error| {
322 ContractFileParseError::FileTooLarge {
323 file_size: file_bytes.len(),
324 }
325 })?;
326 let (header_bytes, after_header_bytes) = file_bytes
327 .split_at_checked(size_of::<ContractFileHeader>())
328 .ok_or(ContractFileParseError::NoHeader)?;
329 let header = unsafe { ContractFileHeader::read_unaligned_unchecked(header_bytes) };
331
332 if header.magic != CONTRACT_FILE_MAGIC {
333 return Err(ContractFileParseError::WrongMagicBytes);
334 }
335
336 if header.read_only_section_file_size > header.read_only_section_memory_size {
337 return Err(ContractFileParseError::InvalidReadOnlySizes {
338 file_size: header.read_only_section_file_size,
339 memory_size: header.read_only_section_memory_size,
340 });
341 }
342
343 let metadata_bytes = file_bytes
344 .get(header.metadata_offset as usize..)
345 .ok_or(ContractFileParseError::MetadataOutOfRange {
346 offset: header.metadata_offset,
347 size: header.metadata_size,
348 file_size,
349 })?
350 .get(..header.metadata_size as usize)
351 .ok_or(ContractFileParseError::MetadataOutOfRange {
352 offset: header.metadata_offset,
353 size: header.metadata_size,
354 file_size,
355 })?;
356
357 let read_only_padding_size =
358 header.read_only_section_memory_size - header.read_only_section_file_size;
359 let read_only_section_offset = ContractFileHeader::SIZE
360 + u32::from(header.num_methods) * ContractFileMethodMetadata::SIZE;
361 let code_section_offset =
362 read_only_section_offset.saturating_add(header.read_only_section_file_size);
363
364 let Some(code_size) = file_size
365 .checked_sub(code_section_offset)
366 .filter(|&code_size| code_size > 0)
367 else {
368 return Err(ContractFileParseError::FileTooSmall {
369 num_methods: header.num_methods,
370 read_only_section_size: header.read_only_section_file_size,
371 file_size,
372 });
373 };
374
375 if header
377 .read_only_section_memory_size
378 .checked_add(code_size)
379 .is_none()
380 {
381 return Err(ContractFileParseError::ContractMemoryTooLarge {
382 read_only_section_memory_size: header.read_only_section_memory_size,
383 code_size,
384 });
385 }
386
387 {
388 let mut contract_file_methods_metadata_iter = {
389 let mut file_contract_metadata_bytes = after_header_bytes;
390
391 iter::repeat_with(move || {
392 let contract_file_method_metadata_bytes = file_contract_metadata_bytes
393 .split_off(..size_of::<ContractFileMethodMetadata>())
394 .ok_or(ContractFileParseError::FileTooSmall {
395 num_methods: header.num_methods,
396 read_only_section_size: header.read_only_section_file_size,
397 file_size,
398 })?;
399 let contract_file_method_metadata = unsafe {
401 ContractFileMethodMetadata::read_unaligned_unchecked(
402 contract_file_method_metadata_bytes,
403 )
404 };
405
406 if contract_file_method_metadata
407 .offset
408 .checked_add(contract_file_method_metadata.size)
409 .is_none_or(|method_end| method_end > file_size)
410 {
411 return Err(ContractFileParseError::FileTooSmall {
412 num_methods: header.num_methods,
413 read_only_section_size: header.read_only_section_file_size,
414 file_size,
415 });
416 }
417
418 if contract_file_method_metadata.offset < code_section_offset
421 || contract_file_method_metadata.offset >= file_size
422 {
423 return Err(ContractFileParseError::MethodOutOfRange {
424 offset: contract_file_method_metadata.offset,
425 code_section_offset,
426 file_size,
427 });
428 }
429
430 Ok(contract_file_method_metadata)
431 })
432 .take(usize::from(header.num_methods))
433 };
434
435 let mut metadata_num_methods = 0;
436 let mut remaining_metadata_bytes = metadata_bytes;
437 let mut metadata_decoder = MetadataDecoder::new(metadata_bytes);
438
439 while let Some(maybe_metadata_item) = metadata_decoder.decode_next() {
440 let metadata_item = maybe_metadata_item?;
441 trace!(?metadata_item, "Decoded metadata item");
442
443 let mut methods_metadata_decoder = metadata_item.into_decoder();
444 loop {
445 let Some(method_metadata_decoder) = methods_metadata_decoder.decode_next()
449 else {
450 break;
451 };
452
453 let before_remaining_bytes = method_metadata_decoder.remaining_metadata_bytes();
454 let (_, method_metadata_item) = method_metadata_decoder.decode_next()?;
455
456 trace!(?method_metadata_item, "Decoded method metadata item");
457 metadata_num_methods += 1;
458
459 let method_metadata_bytes = remaining_metadata_bytes
460 .split_off(
461 ..before_remaining_bytes
462 - methods_metadata_decoder.remaining_metadata_bytes(),
463 )
464 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
465
466 let contract_file_method_metadata = contract_file_methods_metadata_iter
467 .next()
468 .ok_or(ContractFileParseError::InsufficientHeaderMethods {
469 header_num_methods: header.num_methods,
470 metadata_method_index: metadata_num_methods - 1,
471 })??;
472 let address = contract_file_method_metadata.offset - read_only_section_offset
473 + read_only_padding_size;
474
475 if !address.is_multiple_of(u32::from(<ContractInstruction>::ALIGNMENT)) {
476 return Err(ContractFileParseError::MethodUnaligned {
477 file_offset: contract_file_method_metadata.offset,
478 memory_address: address,
479 });
480 }
481
482 contract_method(ContractFileMethod {
483 address,
484 size: contract_file_method_metadata.size,
485 method_metadata_item,
486 method_metadata_bytes,
487 })?;
488 }
489 }
490
491 if metadata_num_methods != header.num_methods {
492 return Err(ContractFileParseError::MetadataNumMethodsMismatch {
493 header_num_methods: header.num_methods,
494 metadata_num_methods,
495 });
496 }
497 }
498
499 if header.host_call_fn_offset != 0 {
500 if header.host_call_fn_offset >= file_size
501 || header.host_call_fn_offset < code_section_offset
502 {
503 return Err(ContractFileParseError::HostCallFnOutOfRange {
504 offset: header.host_call_fn_offset,
505 code_section_offset,
506 file_size,
507 });
508 }
509
510 let instruction_bytes = file_bytes
511 .get(header.host_call_fn_offset as usize..)
512 .ok_or(ContractFileParseError::HostCallFnOutOfRange {
513 offset: header.host_call_fn_offset,
514 code_section_offset,
515 file_size,
516 })?;
517 let instruction = if let Some(instruction_bytes) =
519 unsafe { <Unaligned<u32>>::from_bytes(instruction_bytes) }
520 {
521 instruction_bytes.as_inner()
522 } else {
523 let Some(instruction_bytes) =
525 (unsafe { <Unaligned<u16>>::from_bytes(instruction_bytes) })
526 else {
527 return Err(ContractFileParseError::HostCallFnOutOfRange {
528 offset: header.host_call_fn_offset,
529 code_section_offset,
530 file_size,
531 });
532 };
533
534 u32::from(instruction_bytes.as_inner())
535 };
536
537 let instruction = ContractInstruction::try_decode(instruction)
538 .ok_or(ContractFileParseError::InvalidInstruction { instruction })?;
539
540 if usize::from(instruction.size()) > instruction_bytes.len() {
543 return Err(ContractFileParseError::HostCallFnOutOfRange {
544 offset: header.host_call_fn_offset,
545 code_section_offset,
546 file_size,
547 });
548 }
549
550 #[expect(
553 clippy::rest_pattern_accessible_field,
554 reason = "Do not need other fields"
555 )]
556 let matches_expected_pattern = match instruction {
557 ContractInstruction::Jal { rd, .. } => rd == Register::ZERO,
558 ContractInstruction::CJ { .. } => true,
559 _ => false,
560 };
561
562 if !matches_expected_pattern {
563 return Err(ContractFileParseError::InvalidHostCallFnPattern { instruction });
564 }
565
566 let address =
567 header.host_call_fn_offset - read_only_section_offset + read_only_padding_size;
568
569 if !address.is_multiple_of(u32::from(<ContractInstruction>::ALIGNMENT)) {
570 return Err(ContractFileParseError::HostCallFnUnaligned {
571 file_offset: header.host_call_fn_offset,
572 memory_address: address,
573 });
574 }
575 }
576
577 {
579 let mut offset = code_section_offset as usize;
580
581 while offset < file_bytes.len() {
582 let remaining = &file_bytes[offset..];
583
584 let instruction_word = if remaining.len() >= size_of::<u32>() {
585 u32::from_le_bytes([remaining[0], remaining[1], remaining[2], remaining[3]])
586 } else if remaining.len() >= size_of::<u16>() {
587 u32::from_le_bytes([remaining[0], remaining[1], 0, 0])
588 } else {
589 return Err(ContractFileParseError::UnexpectedTrailingCodeBytes {
591 num_bytes: remaining.len(),
592 });
593 };
594
595 let instruction = <ContractInstruction>::try_decode(instruction_word).ok_or(
596 ContractFileParseError::InvalidInstruction {
597 instruction: instruction_word,
598 },
599 )?;
600
601 if usize::from(instruction.size()) > remaining.len() {
604 return Err(ContractFileParseError::UnexpectedTrailingCodeBytes {
605 num_bytes: remaining.len(),
606 });
607 }
608
609 offset += usize::from(instruction.size());
610 }
611 }
612
613 Ok(Self {
614 read_only_section_file_size: header.read_only_section_file_size,
615 read_only_section_memory_size: header.read_only_section_memory_size,
616 num_methods: header.num_methods,
617 bytes: file_bytes,
618 })
619 }
620
621 pub unsafe fn parse_unchecked(file_bytes: &'a [u8]) -> Self {
629 let header = unsafe { ContractFileHeader::read_unaligned_unchecked(file_bytes) };
631
632 Self {
633 read_only_section_file_size: header.read_only_section_file_size,
634 read_only_section_memory_size: header.read_only_section_memory_size,
635 num_methods: header.num_methods,
636 bytes: file_bytes,
637 }
638 }
639
640 #[inline(always)]
642 pub fn header(&self) -> ContractFileHeader {
643 unsafe { ContractFileHeader::read_unaligned_unchecked(self.bytes) }
645 }
646
647 #[inline]
649 pub fn metadata_bytes(&self) -> &[u8] {
650 let header = self.header();
651 unsafe {
653 self.bytes
654 .get_unchecked(header.metadata_offset as usize..)
655 .get_unchecked(..header.metadata_size as usize)
656 }
657 }
658
659 #[inline]
661 pub fn contract_memory_size(&self) -> u32 {
662 let read_only_section_offset = ContractFileHeader::SIZE
663 + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
664 let read_only_padding_size =
665 self.read_only_section_memory_size - self.read_only_section_file_size;
666 self.bytes.len() as u32 - read_only_section_offset + read_only_padding_size
667 }
668
669 #[must_use = "Must check that contract memory was large enough"]
674 pub fn initialize_contract_memory(&self, mut contract_memory: BorrowedCursor<'_, u8>) -> bool {
675 let contract_memory_input_size = contract_memory.capacity();
676 let read_only_section_offset = ContractFileHeader::SIZE
677 + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
678 let read_only_padding_size =
679 self.read_only_section_memory_size - self.read_only_section_file_size;
680
681 let source_bytes = unsafe {
683 self.bytes
684 .get_unchecked(read_only_section_offset as usize..)
685 };
686
687 let mut contract_memory_bytes = unsafe { contract_memory.as_mut() };
689
690 if contract_memory_bytes.len() == source_bytes.len() {
692 contract_memory_bytes.write_copy_of_slice(source_bytes);
693 unsafe {
695 contract_memory.advance(contract_memory_input_size);
696 }
697 return true;
698 }
699
700 let Some(read_only_file_target_bytes) =
701 contract_memory_bytes.split_off_mut(..self.read_only_section_file_size as usize)
702 else {
703 trace!(
704 %contract_memory_input_size,
705 contract_memory_size = %self.contract_memory_size(),
706 read_only_section_file_size = self.read_only_section_file_size,
707 "Not enough bytes to write read-only section from the file"
708 );
709
710 return false;
711 };
712
713 let (read_only_file_source_bytes, code_source_bytes) =
715 unsafe { source_bytes.split_at_unchecked(self.read_only_section_file_size as usize) };
716 read_only_file_target_bytes.write_copy_of_slice(read_only_file_source_bytes);
718
719 let Some(read_only_padding_bytes) =
720 contract_memory_bytes.split_off_mut(..read_only_padding_size as usize)
721 else {
722 trace!(
723 %contract_memory_input_size,
724 contract_memory_size = %self.contract_memory_size(),
725 read_only_section_file_size = self.read_only_section_file_size,
726 read_only_section_memory_size = self.read_only_section_memory_size,
727 %read_only_padding_size,
728 "Not enough bytes to write read-only padding section"
729 );
730
731 return false;
732 };
733
734 read_only_padding_bytes.write_filled(0);
736
737 if code_source_bytes.len() != contract_memory_bytes.len() {
738 trace!(
739 %contract_memory_input_size,
740 contract_memory_size = %self.contract_memory_size(),
741 read_only_section_file_size = self.read_only_section_file_size,
742 read_only_section_memory_size = self.read_only_section_memory_size,
743 %read_only_padding_size,
744 code_size = %code_source_bytes.len(),
745 "Not enough bytes to write code section from the file"
746 );
747
748 return false;
749 }
750
751 contract_memory_bytes.write_copy_of_slice(code_source_bytes);
752 unsafe {
754 contract_memory.advance(contract_memory_input_size);
755 }
756
757 true
758 }
759
760 pub fn get_code(&self) -> &[u8] {
762 let read_only_section_offset = ContractFileHeader::SIZE
763 + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
764
765 let source_bytes = unsafe {
767 self.bytes
768 .get_unchecked(read_only_section_offset as usize..)
769 };
770
771 let (_read_only_file_source_bytes, code_source_bytes) =
773 unsafe { source_bytes.split_at_unchecked(self.read_only_section_file_size as usize) };
774
775 code_source_bytes
776 }
777
778 pub fn iterate_methods(
780 &self,
781 ) -> impl ExactSizeIterator<Item = ContractFileMethod<'_>> + TrustedLen {
782 let metadata_bytes = self.metadata_bytes();
783
784 #[ouroboros::self_referencing]
785 struct MethodsMetadataIterState<'metadata> {
786 metadata_decoder: MetadataDecoder<'metadata>,
787 #[borrows(mut metadata_decoder)]
788 #[covariant]
789 methods_metadata_decoder: Option<MethodsMetadataDecoder<'this, 'metadata>>,
790 }
791
792 let metadata_decoder = MetadataDecoder::new(metadata_bytes);
793
794 let mut methods_metadata_state =
795 MethodsMetadataIterState::new(metadata_decoder, |metadata_decoder| {
796 metadata_decoder
797 .decode_next()
798 .and_then(Result::ok)
799 .map(MetadataItem::into_decoder)
800 });
801
802 let mut metadata_methods_iter = iter::from_fn(move || {
803 loop {
804 let maybe_next_item = methods_metadata_state.with_methods_metadata_decoder_mut(
805 |maybe_methods_metadata_decoder| {
806 let methods_metadata_decoder = maybe_methods_metadata_decoder.as_mut()?;
807 let method_metadata_decoder = methods_metadata_decoder.decode_next()?;
808
809 let before_remaining_bytes =
810 method_metadata_decoder.remaining_metadata_bytes();
811
812 let (_, method_metadata_item) = method_metadata_decoder
813 .decode_next()
814 .expect("Input is valid according to function contract; qed");
815
816 let method_metadata_bytes = unsafe {
818 metadata_bytes
819 .get_unchecked(metadata_bytes.len() - before_remaining_bytes..)
820 .get_unchecked(
821 ..before_remaining_bytes
822 - methods_metadata_decoder.remaining_metadata_bytes(),
823 )
824 };
825
826 Some((method_metadata_item, method_metadata_bytes))
827 },
828 );
829
830 if let Some(next_item) = maybe_next_item {
831 return Some(next_item);
832 }
833
834 replace_with_or_abort(&mut methods_metadata_state, |methods_metadata_state| {
836 let metadata_decoder = methods_metadata_state.into_heads().metadata_decoder;
837 MethodsMetadataIterState::new(metadata_decoder, |metadata_decoder| {
838 metadata_decoder
839 .decode_next()
840 .and_then(Result::ok)
841 .map(MetadataItem::into_decoder)
842 })
843 });
844
845 if methods_metadata_state
846 .borrow_methods_metadata_decoder()
847 .is_none()
848 {
849 return None;
850 }
851 }
852 });
853
854 let read_only_section_offset = ContractFileHeader::SIZE
855 + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
856 let read_only_padding_size =
857 self.read_only_section_memory_size - self.read_only_section_file_size;
858 let contract_file_methods_metadata_bytes =
860 unsafe { self.bytes.get_unchecked(size_of::<ContractFileHeader>()..) };
861
862 (0..self.num_methods).map(move |method_index| {
863 let contract_file_method_metadata_bytes = unsafe {
865 contract_file_methods_metadata_bytes
866 .get_unchecked(
867 method_index as usize * size_of::<ContractFileMethodMetadata>()..,
868 )
869 .get_unchecked(..size_of::<ContractFileMethodMetadata>())
870 };
871 let contract_file_method_metadata = unsafe {
873 ContractFileMethodMetadata::read_unaligned_unchecked(
874 contract_file_method_metadata_bytes,
875 )
876 };
877
878 let (method_metadata_item, method_metadata_bytes) = metadata_methods_iter
879 .next()
880 .expect("Protected internal invariant checked in constructor; qed");
881
882 ContractFileMethod {
883 address: contract_file_method_metadata.offset - read_only_section_offset
884 + read_only_padding_size,
885 size: contract_file_method_metadata.size,
886 method_metadata_item,
887 method_metadata_bytes,
888 }
889 })
890 }
891}