use crate::{
gas_metering::{CustomConstantCostRules, Rules},
ids::{CodeId, prelude::*},
};
use alloc::vec::Vec;
use gear_wasm_instrument::{GEAR_SUPPORTED_FEATURES, InstrumentationBuilder, Module};
mod errors;
mod instrumented;
mod metadata;
mod utils;
pub use errors::*;
pub use instrumented::*;
pub use metadata::*;
pub use utils::{ALLOWED_EXPORTS, MAX_WASM_PAGES_AMOUNT, REQUIRED_EXPORTS};
use utils::CodeTypeSectionSizes;
const GENERIC_OS_PAGE_SIZE: u32 = 4096;
pub struct TryNewCodeConfig {
pub version: u32,
pub stack_height: Option<u32>,
pub data_segments_amount_limit: Option<u32>,
pub table_amount_limit: Option<u32>,
pub type_section_len_limit: Option<u32>,
pub type_section_params_per_type_limit: Option<u32>,
pub export_stack_height: bool,
pub check_exports: bool,
pub check_imports: bool,
pub check_and_canonize_stack_end: bool,
pub check_mut_global_exports: bool,
pub check_start_section: bool,
pub check_data_section: bool,
pub check_table_section: bool,
pub make_validation: bool,
}
impl TryNewCodeConfig {
pub fn new_no_exports_check() -> Self {
Self {
check_exports: false,
..Default::default()
}
}
}
impl Default for TryNewCodeConfig {
fn default() -> Self {
Self {
version: 1,
stack_height: None,
data_segments_amount_limit: None,
table_amount_limit: None,
type_section_len_limit: None,
type_section_params_per_type_limit: None,
export_stack_height: false,
check_exports: true,
check_imports: true,
check_and_canonize_stack_end: true,
check_mut_global_exports: true,
check_start_section: true,
check_data_section: true,
check_table_section: true,
make_validation: true,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Code {
original: Vec<u8>,
instrumented: InstrumentedCode,
metadata: CodeMetadata,
}
impl Code {
fn try_new_internal<R, GetRulesFn>(
original_code: Vec<u8>,
get_gas_rules: Option<GetRulesFn>,
config: TryNewCodeConfig,
) -> Result<Self, CodeError>
where
R: Rules,
GetRulesFn: FnMut(&Module) -> R,
{
if config.make_validation {
wasmparser::Validator::new_with_features(GEAR_SUPPORTED_FEATURES)
.validate_all(&original_code)
.map_err(CodeError::Validation)?;
}
let mut module = Module::new(&original_code)?;
let static_pages = utils::get_static_pages(&module)?;
let stack_end = match config.check_and_canonize_stack_end {
true => utils::check_and_canonize_gear_stack_end(&mut module, static_pages)?,
false => None,
};
if config.check_data_section {
utils::check_data_section(
&module,
static_pages,
stack_end,
config.data_segments_amount_limit,
)?;
}
if config.check_mut_global_exports {
utils::check_mut_global_exports(&module)?;
}
if config.check_start_section {
utils::check_start_section(&module)?;
}
if config.check_exports {
utils::check_exports(&module)?;
}
if config.check_imports {
utils::check_imports(&module)?;
}
if let Some(limit) = config.type_section_params_per_type_limit {
utils::check_type_section(&module, limit)?;
}
let exports = utils::get_exports(&module);
let mut instrumentation_builder = InstrumentationBuilder::new("env");
if let Some(stack_limit) = config.stack_height {
instrumentation_builder.with_stack_limiter(stack_limit, config.export_stack_height);
}
if let Some(get_gas_rules) = get_gas_rules {
instrumentation_builder.with_gas_limiter(get_gas_rules);
}
module = instrumentation_builder.instrument(module)?;
let data_section_size = utils::get_data_section_size(&module)?;
let global_section_size = utils::get_instantiated_global_section_size(&module)?;
let table_section_size = utils::get_instantiated_table_section_size(&module);
let element_section_size = utils::get_instantiated_element_section_size(&module)?;
let code = module.serialize()?;
let CodeTypeSectionSizes {
code_section,
type_section,
} = utils::get_code_type_sections_sizes(&code, config.type_section_len_limit)?;
let instantiated_section_sizes = InstantiatedSectionSizes::new(
code_section,
data_section_size,
global_section_size,
table_section_size,
element_section_size,
type_section,
);
let instrumented_code = InstrumentedCode::new(code, instantiated_section_sizes);
let metadata = CodeMetadata::new(
original_code.len() as u32,
exports.clone(),
static_pages,
stack_end,
InstrumentationStatus::Instrumented {
version: config.version,
code_len: instrumented_code.bytes().len() as u32,
},
);
Ok(Self {
original: original_code,
instrumented: instrumented_code,
metadata,
})
}
pub fn try_new<R, GetRulesFn>(
original_code: Vec<u8>,
version: u32,
get_gas_rules: GetRulesFn,
stack_height: Option<u32>,
data_segments_amount_limit: Option<u32>,
type_section_len_limit: Option<u32>,
type_section_params_per_type_limit: Option<u32>,
) -> Result<Self, CodeError>
where
R: Rules,
GetRulesFn: FnMut(&Module) -> R,
{
Self::try_new_internal(
original_code,
Some(get_gas_rules),
TryNewCodeConfig {
version,
stack_height,
data_segments_amount_limit,
type_section_len_limit,
type_section_params_per_type_limit,
..Default::default()
},
)
}
pub fn try_new_mock_const_or_no_rules(
original_code: Vec<u8>,
const_rules: bool,
config: TryNewCodeConfig,
) -> Result<Self, CodeError> {
let get_gas_rules =
const_rules.then_some(|_module: &Module| CustomConstantCostRules::default());
Self::try_new_internal(original_code, get_gas_rules, config)
}
pub fn try_new_mock_with_rules<R, GetRulesFn>(
original_code: Vec<u8>,
get_gas_rules: GetRulesFn,
config: TryNewCodeConfig,
) -> Result<Self, CodeError>
where
R: Rules,
GetRulesFn: FnMut(&Module) -> R,
{
Self::try_new_internal(original_code, Some(get_gas_rules), config)
}
pub fn original_code(&self) -> &[u8] {
&self.original
}
pub fn instrumented_code(&self) -> &InstrumentedCode {
&self.instrumented
}
pub fn metadata(&self) -> &CodeMetadata {
&self.metadata
}
pub fn into_parts(self) -> (Vec<u8>, InstrumentedCode, CodeMetadata) {
(self.original, self.instrumented, self.metadata)
}
pub fn into_instrumented_code_and_metadata(self) -> InstrumentedCodeAndMetadata {
InstrumentedCodeAndMetadata {
instrumented_code: self.instrumented,
metadata: self.metadata,
}
}
}
#[derive(Clone, Debug)]
pub struct CodeAndId {
code: Code,
code_id: CodeId,
}
impl CodeAndId {
pub fn new(code: Code) -> Self {
let code_id = CodeId::generate(code.original_code());
Self { code, code_id }
}
pub fn from_parts_unchecked(code: Code, code_id: CodeId) -> Self {
debug_assert_eq!(code_id, CodeId::generate(code.original_code()));
Self { code, code_id }
}
pub unsafe fn from_incompatible_parts(code: Code, code_id: CodeId) -> Self {
Self { code, code_id }
}
pub fn code_id(&self) -> CodeId {
self.code_id
}
pub fn code(&self) -> &Code {
&self.code
}
pub fn into_parts(self) -> (Code, CodeId) {
(self.code, self.code_id)
}
}
#[derive(Clone, Debug)]
pub struct InstrumentedCodeAndMetadata {
pub instrumented_code: InstrumentedCode,
pub metadata: CodeMetadata,
}
impl InstrumentedCodeAndMetadata {
pub fn into_parts(self) -> (InstrumentedCode, CodeMetadata) {
(self.instrumented_code, self.metadata)
}
}
impl From<Code> for InstrumentedCodeAndMetadata {
fn from(code: Code) -> Self {
let (_, instrumented_code, metadata) = code.into_parts();
Self {
instrumented_code,
metadata,
}
}
}
#[cfg(test)]
mod tests {
use crate::{
code::{
Code, CodeError, DataSectionError, ExportError, GENERIC_OS_PAGE_SIZE, ImportError,
StackEndError, TryNewCodeConfig, TypeSectionError, utils::REF_TYPE_SIZE,
},
gas_metering::CustomConstantCostRules,
};
use alloc::{format, vec::Vec};
use gear_wasm_instrument::{InstrumentationError, ModuleError, STACK_END_EXPORT_NAME};
fn wat2wasm_with_validate(s: &str, validate: bool) -> Vec<u8> {
let code = wat::parse_str(s).unwrap();
if validate {
wasmparser::validate(&code).unwrap();
}
code
}
fn wat2wasm(s: &str) -> Vec<u8> {
wat2wasm_with_validate(s, true)
}
macro_rules! assert_code_err {
($res:expr, $expected:pat) => {
let err = $res.expect_err("Code::try_new must return an error");
let expected_err = stringify!($expected);
assert!(
matches!(err, $expected),
"Must receive {:?}, got {:?}",
expected_err,
err
);
};
}
fn try_new_code_from_wat_with_params(
wat: &str,
stack_height: Option<u32>,
data_segments_amount_limit: Option<u32>,
make_validation: bool,
) -> Result<Code, CodeError> {
Code::try_new_mock_const_or_no_rules(
wat2wasm(wat),
true,
TryNewCodeConfig {
stack_height,
data_segments_amount_limit,
make_validation,
..Default::default()
},
)
}
fn try_new_code_from_wat(wat: &str, stack_height: Option<u32>) -> Result<Code, CodeError> {
try_new_code_from_wat_with_params(wat, stack_height, None, true)
}
#[test]
fn reject_unknown_exports() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "this_import_is_unknown" (func $test))
(func $test)
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Export(ExportError::ExcessExport(0))
);
}
#[test]
fn required_fn_not_found() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "handle_signal" (func $handle_signal))
(func $handle_signal)
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Export(ExportError::RequiredExportNotFound)
);
}
#[test]
fn stack_limit_injection_works() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "init" (func $init))
(func $init)
)
"#;
let _ = try_new_code_from_wat(wat, Some(16 * 1024)).unwrap();
}
#[test]
fn data_segment_out_of_static_memory() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "init" (func $init))
(func $init)
(data (;0;) (i32.const 0x10000) "gear")
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
0x10000, 0x10003, 0x10000
))
);
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "init" (func $init))
(func $init)
(data (;0;) (i32.const 0xfffd) "gear")
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
0xfffd, 0x10000, 0x10000
))
);
}
#[test]
fn data_segment_out_of_u32() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(export "init" (func $init))
(func $init)
(data (;0;) (i32.const 0xffffffff) "gear")
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::DataSection(DataSectionError::EndAddressOverflow(0xffffffff))
);
}
#[test]
fn data_segment_stack_overlaps() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 3))
(export "init" (func $init))
(func $init)
(data (;0;) (i32.const 0x10000) "gear")
(export "{STACK_END_EXPORT_NAME}" (global 0))
(global (mut i32) (i32.const 0x20000))
)"#
);
assert_code_err!(
try_new_code_from_wat(wat.as_str(), None),
CodeError::DataSection(DataSectionError::GearStackOverlaps(0x10000, 0x20000))
);
}
#[test]
fn data_section() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 3))
(export "init" (func $init))
(func $init)
(data (i32.const 0x20000) "gear")
(data (i32.const 0x10000) "") ;; empty data segment
(data (i32.const 0x1ffff) "gear") ;; overlapping other segments, also ok
(data (i32.const 0x2ffff) "g") ;; one byte before the end of memory
(export "{STACK_END_EXPORT_NAME}" (global 0))
(global (mut i32) (i32.const 0x10000))
)"#
);
try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
}
#[test]
fn check_mutable_global_exports_restriction() {
let wat = r#"
(module
(import "env" "memory" (memory 0))
(func $init)
(export "init" (func $init))
(export "global" (global 0))
(global (;0;) (mut i32) (i32.const 0))
)"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Export(ExportError::MutableGlobalExport(0, 1))
);
}
#[test]
fn stack_end_initialization() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 1))
(import "env" "unknown" (global i32))
(func $init)
(export "init" (func $init))
(export "{STACK_END_EXPORT_NAME}" (global 1))
(global (mut i32) (global.get 0))
)"#
);
assert_code_err!(
try_new_code_from_wat(wat.as_str(), None),
CodeError::StackEnd(StackEndError::Initialization)
);
}
#[test]
fn stack_end_alignment() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 2))
(func $init)
(export "init" (func $init))
(export "{STACK_END_EXPORT_NAME}" (global 0))
(global (;0;) (mut i32) (i32.const 0x10001))
)"#
);
assert_code_err!(
try_new_code_from_wat(wat.as_str(), None),
CodeError::StackEnd(StackEndError::NotAligned(0x10001))
);
}
#[test]
fn stack_end_out_of_static_memory() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 1))
(func $init)
(export "init" (func $init))
(export "{STACK_END_EXPORT_NAME}" (global 0))
(global (;0;) (mut i32) (i32.const 0x20000))
)"#
);
assert_code_err!(
try_new_code_from_wat(wat.as_str(), None),
CodeError::StackEnd(StackEndError::OutOfStatic(0x20000, 0x10000))
);
}
#[test]
fn stack_end() {
let wat = format!(
r#"
(module
(import "env" "memory" (memory 1))
(func $init)
(export "init" (func $init))
(export "{STACK_END_EXPORT_NAME}" (global 0))
(global (;0;) (mut i32) (i32.const 0x10000))
)"#
);
let code = try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
assert_eq!(code.metadata().stack_end(), Some(1.into()));
}
#[test]
fn export_to_imported_function() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(import "env" "gr_leave" (func $gr_leave))
(export "init" (func $gr_leave))
(func)
)"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Export(ExportError::ExportReferencesToImportFunction(0, 0))
);
}
#[test]
fn export_to_imported_global() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(import "env" "global" (global i32))
(export "init" (func 0))
(export "global" (global 0))
(func)
)"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Export(ExportError::ExportReferencesToImportGlobal(1, 0))
);
}
#[test]
fn multi_memory_import() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(import "env" "memory2" (memory 2))
(export "init" (func $init))
(func $init)
)
"#;
let res = Code::try_new(
wat2wasm_with_validate(wat, false),
1,
|_| CustomConstantCostRules::default(),
None,
None,
None,
None,
);
assert_code_err!(res, CodeError::Validation(_));
}
#[test]
fn global_import() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(import "env" "unknown" (global $unknown i32))
(export "init" (func $init))
(func $init)
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Import(ImportError::UnexpectedImportKind {
kind: &"Global",
index: 1
})
);
}
#[test]
fn table_import() {
let wat = r#"
(module
(import "env" "memory" (memory 1))
(import "env" "unknown" (table $unknown 10 20 funcref))
(export "init" (func $init))
(func $init)
)
"#;
assert_code_err!(
try_new_code_from_wat(wat, None),
CodeError::Import(ImportError::UnexpectedImportKind {
kind: &"Table",
index: 1
})
);
}
#[test]
fn data_segments_amount_limit() {
const DATA_SEGMENTS_AMOUNT_LIMIT: u32 = 1024;
let segment = r#"(data (i32.const 0x0) "gear")"#;
let wat = format!(
r#"
(module
(import "env" "memory" (memory 1))
(func $init)
(export "init" (func $init))
{}
)
"#,
segment.repeat(1025)
);
assert_code_err!(
try_new_code_from_wat_with_params(
wat.as_str(),
None,
DATA_SEGMENTS_AMOUNT_LIMIT.into(),
true,
),
CodeError::DataSection(DataSectionError::DataSegmentsAmountLimit {
limit: DATA_SEGMENTS_AMOUNT_LIMIT,
actual: 1025
})
);
}
#[test]
fn type_section_limits() {
const TYPE_SECTION_LEN_LIMIT: u32 = 16;
const PARAMS_PER_TYPE_LIMIT: u32 = 10;
fn try_new_with_type_limits(
wat: &str,
type_section_len_limit: Option<u32>,
type_section_params_per_type_limit: Option<u32>,
) -> Result<Code, CodeError> {
Code::try_new_mock_const_or_no_rules(
wat2wasm(wat),
true,
TryNewCodeConfig {
type_section_len_limit,
type_section_params_per_type_limit,
stack_height: None,
make_validation: true,
..Default::default()
},
)
}
let wat = r#"
(module
(import "env" "memory" (memory 1))
(func $init)
(export "init" (func $init))
(type (func (param i64 i64 i32 i32 i64 i32 i64 i64 i64 i32 i32 i64 i32 i64) (result i64)))
)"#;
assert_code_err!(
try_new_with_type_limits(wat, TYPE_SECTION_LEN_LIMIT.into(), None,),
CodeError::TypeSection(TypeSectionError::LengthLimitExceeded {
limit: TYPE_SECTION_LEN_LIMIT,
actual: 26,
})
);
assert_code_err!(
try_new_with_type_limits(wat, None, PARAMS_PER_TYPE_LIMIT.into(),),
CodeError::TypeSection(TypeSectionError::ParametersPerTypeLimitExceeded {
limit: PARAMS_PER_TYPE_LIMIT,
actual: 14,
})
);
}
#[test]
fn data_section_bytes() {
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x20000) "gear")
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE,
);
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x0000) "gear")
(data (i32.const 0x1000) "gear")
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE * 2,
);
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x0000) "gear")
(data (i32.const 0x10000) "gear")
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE * 2,
);
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x0) "")
(data (i32.const 0x0) "")
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
0,
);
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x20000) "gear")
(data (i32.const 0x20001) "gear")
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE,
);
let wat = format!(
r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x20000) "{}")
)
"#,
"a".repeat((GENERIC_OS_PAGE_SIZE + 1) as usize)
);
assert_eq!(
try_new_code_from_wat(&wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE * 2,
);
let wat = format!(
r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x20000) "{0}")
(data (i32.const 0x23000) "{1}")
)
"#,
"a".repeat((GENERIC_OS_PAGE_SIZE * 3) as usize),
"b".repeat((GENERIC_OS_PAGE_SIZE) as usize)
);
assert_eq!(
try_new_code_from_wat(&wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE * 4,
);
let wat = format!(
r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(data (i32.const 0x20000) "{0}")
(data (i32.const 0x21000) "{1}")
)
"#,
"a".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize),
"b".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize)
);
assert_eq!(
try_new_code_from_wat(&wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.data_section(),
GENERIC_OS_PAGE_SIZE * 4,
);
}
#[test]
fn code_section_bytes() {
const INSTRUMENTATION_CODE_SIZE: u32 = 74;
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(func $sum (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add
)
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.code_section(),
INSTRUMENTATION_CODE_SIZE + 11,
);
}
#[test]
fn global_section_bytes() {
const INSTRUMENTATION_GLOBALS_SIZE: usize = size_of::<i32>() + size_of::<i64>();
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(global (mut i32) (i32.const 0))
(global (mut i32) (i32.const 0))
(global (mut i64) (i64.const 0))
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.global_section(),
(INSTRUMENTATION_GLOBALS_SIZE + size_of::<i32>() * 2 + size_of::<i64>()) as u32,
);
}
#[test]
fn element_section_bytes() {
let wat = r#"
(module
(import "env" "memory" (memory 3))
(func $init)
(export "init" (func $init))
(table 10 10 funcref)
(elem (i32.const 1) 0 0 0 0)
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.table_section(),
10 * REF_TYPE_SIZE,
);
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.element_section(),
REF_TYPE_SIZE * 4,
);
}
#[test]
fn type_section_bytes() {
let wat = r#"
(module
(import "env" "memory" (memory 3))
(type (;35;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)))
(type (;36;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)))
(func $init)
(export "init" (func $init))
)
"#;
assert_eq!(
try_new_code_from_wat(wat, Some(1024))
.unwrap()
.instrumented_code()
.instantiated_section_sizes()
.type_section(),
50,
);
}
#[test]
fn unsupported_instruction() {
let res = try_new_code_from_wat_with_params(
r#"
(module
(import "env" "memory" (memory 0 1))
(func (result f64)
f64.const 10
f64.const 3
f64.div)
(global i32 (i32.const 42))
(func $init)
(export "init" (func $init))
)
"#,
Some(1024),
None,
false,
);
assert!(matches!(
res,
Err(CodeError::Module(ModuleError::UnsupportedInstruction(_))),
));
let res = try_new_code_from_wat(
r#"
(module
(import "env" "memory" (memory 0 1))
(func (result i32)
global.get 0
memory.grow
)
(global i32 (i32.const 42))
(func $init)
(export "init" (func $init))
)"#,
Some(1024),
);
assert!(matches!(
res,
Err(CodeError::Instrumentation(
InstrumentationError::GasInjection
))
));
}
}