diff --git a/Cargo.toml b/Cargo.toml index f0b8f18..c5b07aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ env_logger = "0.4" lazy_static = "0.2" clap = "2.24" glob = "0.2" +byteorder = "1" [lib] diff --git a/build/src/main.rs b/build/src/main.rs index 9cf3a1e..71b23b9 100644 --- a/build/src/main.rs +++ b/build/src/main.rs @@ -76,6 +76,14 @@ fn main() { .arg(Arg::with_name("skip_alloc") .help("Skip allocator externalizer step producing final wasm") .long("skip-externalize")) + .arg(Arg::with_name("runtime_type") + .help("Injects RUNTIME_TYPE global export") + .takes_value(true) + .long("runtime-type")) + .arg(Arg::with_name("runtime_version") + .help("Injects RUNTIME_VERSION global export") + .takes_value(true) + .long("runtime-version")) .get_matches(); let target_dir = matches.value_of("target").expect("is required; qed"); @@ -98,5 +106,15 @@ fn main() { wasm_utils::optimize(&mut module, vec!["_call", "setTempRet0"]).expect("Optimizer to finish without errors"); } + if let Some(runtime_type) = matches.value_of("runtime_type") { + let runtime_type: &[u8] = runtime_type.as_bytes(); + if runtime_type.len() != 4 { + panic!("--runtime-type should be equal to 4 bytes"); + } + let runtime_version: u32 = matches.value_of("runtime_version").unwrap_or("1").parse() + .expect("--runtime-version should be a positive integer"); + module = wasm_utils::inject_runtime_type(module, &runtime_type, runtime_version); + } + parity_wasm::serialize_to_file(&path, module).unwrap(); -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index c565594..12d0a9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ extern crate parity_wasm; extern crate env_logger; +extern crate byteorder; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; @@ -12,6 +13,7 @@ mod logger; mod ext; mod pack; mod nondeterminism_check; +mod runtime_type; pub use optimizer::{optimize, Error as OptimizerError}; pub use gas::inject_gas_counter; @@ -19,3 +21,4 @@ pub use logger::init_log; pub use ext::externalize; pub use pack::pack_instance; pub use nondeterminism_check::is_deterministic; +pub use runtime_type::inject_runtime_type; diff --git a/src/nondeterminism_check.rs b/src/nondeterminism_check.rs index b8cc7ca..eae6f1a 100644 --- a/src/nondeterminism_check.rs +++ b/src/nondeterminism_check.rs @@ -120,7 +120,7 @@ mod tests { #[test] fn nondeterminism_not() { let module = builder::module() - .function().signature().return_type().f32().build() + .function().signature().return_type().i32().build() .body() .with_opcodes(elements::Opcodes::new( vec![ diff --git a/src/runtime_type.rs b/src/runtime_type.rs new file mode 100644 index 0000000..24443ef --- /dev/null +++ b/src/runtime_type.rs @@ -0,0 +1,43 @@ +use parity_wasm::{elements, builder}; +use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Opcode, Internal }; +use byteorder::{ LittleEndian, ByteOrder }; + +pub fn inject_runtime_type(module: Module, runtime_type: &[u8], runtime_version: u32) -> Module { + let runtime_type: u32 = LittleEndian::read_u32(&runtime_type); + let globals_count: u32 = match module.global_section() { + Some(ref section) => section.entries().len() as u32, + None => 0 + }; + let imported_globals_count: u32 = match module.import_section() { + Some(ref section) => section.entries().iter().filter(|e| match *e.external() { + External::Global(ref _a) => true, + _ => false + }).count() as u32, + None => 0 + }; + let total_globals_count: u32 = globals_count + imported_globals_count; + + builder::from_module(module) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_type as i32), Opcode::End]))) + .with_export(ExportEntry::new("RUNTIME_TYPE".into(), Internal::Global(total_globals_count))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_version as i32), Opcode::End]))) + .with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1))) + .build() +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn it_injects() { + let mut module = builder::module() + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(42 as i32)]))) + .build(); + module = inject_runtime_type(module, b"emcc", 1); + let global_section = module.global_section().expect("Global section expected"); + assert_eq!(3, global_section.entries().len()); + let export_section = module.export_section().expect("Export section expected"); + assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_TYPE" ).is_some()); + assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_VERSION" ).is_some()); + } +}