소스 검색

Use `wasm-tools` instead of `parity-wasm` (#1250)

Cyrill Leutwiler 2 년 전
부모
커밋
1607343b07
2개의 변경된 파일86개의 추가작업 그리고 45개의 파일을 삭제
  1. 2 1
      Cargo.toml
  2. 84 44
      src/linker/wasm.rs

+ 2 - 1
Cargo.toml

@@ -21,7 +21,6 @@ rand = "0.8"
 num-bigint = { version = "0.4", features = ["rand"]}
 num-traits = "0.2"
 num-integer = "0.1"
-parity-wasm = "0.45"
 clap = "4.1"
 clap_complete = "4.1"
 hex = "0.4"
@@ -58,6 +57,8 @@ parity-scale-codec = "3.4"
 ink = "4.1.0"
 scale-info = "2.4"
 petgraph = "0.6.3"
+wasmparser = "0.102.0"
+wasm-encoder = "0.25"
 
 [dev-dependencies]
 num-derive = "0.3"

+ 84 - 44
src/linker/wasm.rs

@@ -1,12 +1,15 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use parity_wasm::builder;
-use parity_wasm::elements::{InitExpr, Instruction, Module};
 use std::ffi::CString;
 use std::fs::File;
 use std::io::Read;
 use std::io::Write;
 use tempfile::tempdir;
+use wasm_encoder::{
+    ConstExpr, DataSection, DataSegment, DataSegmentMode, EntityType, GlobalSection, GlobalType,
+    ImportSection, MemoryType, Module, RawSection, ValType,
+};
+use wasmparser::{Data, DataKind, Global, Import, Parser, Payload::*, SectionLimited, TypeRef};
 
 pub fn link(input: &[u8], name: &str) -> Vec<u8> {
     let dir = tempdir().expect("failed to create temp directory for linking");
@@ -60,55 +63,92 @@ pub fn link(input: &[u8], name: &str) -> Vec<u8> {
         .read_to_end(&mut output)
         .expect("failed to read output file");
 
-    let mut module: Module =
-        parity_wasm::deserialize_buffer(&output).expect("cannot deserialize llvm wasm");
+    generate_module(&output)
+}
 
-    {
-        let imports = module.import_section_mut().unwrap().entries_mut();
-        let mut ind = 0;
-
-        while ind < imports.len() {
-            if imports[ind].field().starts_with("seal") {
-                let module_name = match imports[ind].field() {
-                    "seal_set_storage" => "seal2",
-                    "seal_clear_storage"
-                    | "seal_contains_storage"
-                    | "seal_get_storage"
-                    | "seal_instantiate"
-                    | "seal_terminate"
-                    | "seal_call" => "seal1",
-                    _ => "seal0",
-                };
-                *imports[ind].module_mut() = module_name.to_owned();
-            } else if imports[ind].field() == "instantiation_nonce" {
-                *imports[ind].module_mut() = "seal0".to_owned();
+fn generate_module(input: &[u8]) -> Vec<u8> {
+    let mut module = Module::new();
+    for payload in Parser::new(0).parse_all(input).map(|s| s.unwrap()) {
+        match payload {
+            DataSection(s) => generate_data_section(s, &mut module),
+            ImportSection(s) => generate_import_section(s, &mut module),
+            GlobalSection(s) => generate_global_section(s, &mut module),
+            ModuleSection { .. } | ComponentSection { .. } => panic!("nested WASM module"),
+            _ => {
+                if let Some((id, range)) = payload.as_section() {
+                    module.section(&RawSection {
+                        id,
+                        data: &input[range],
+                    });
+                }
             }
-
-            ind += 1;
         }
     }
+    module.finish()
+}
 
-    // remove empty initializers
-    if let Some(data_section) = module.data_section_mut() {
-        let entries = data_section.entries_mut();
-        let mut index = 0;
-
-        while index < entries.len() {
-            if entries[index].value().iter().all(|b| *b == 0) {
-                entries.remove(index);
-            } else {
-                index += 1;
-            }
-        }
+/// Resolve all pallet contracts runtime imports
+fn generate_import_section(section: SectionLimited<Import>, module: &mut Module) {
+    let mut imports = ImportSection::new();
+    for import in section.into_iter().map(|import| import.unwrap()) {
+        let import_type = match import.ty {
+            TypeRef::Func(n) => EntityType::Function(n),
+            TypeRef::Memory(m) => EntityType::Memory(MemoryType {
+                maximum: m.maximum,
+                minimum: m.initial,
+                memory64: m.memory64,
+                shared: m.shared,
+            }),
+            _ => panic!("unexpected WASM import section {:?}", import),
+        };
+        let module_name = match import.name {
+            "memory" => import.module,
+            "seal_set_storage" => "seal2",
+            "seal_clear_storage"
+            | "seal_contains_storage"
+            | "seal_get_storage"
+            | "seal_instantiate"
+            | "seal_terminate"
+            | "seal_call" => "seal1",
+            _ => "seal0",
+        };
+        imports.import(module_name, import.name, import_type);
     }
+    module.section(&imports);
+}
 
-    // set stack pointer to 64k (there is only one global)
-    for global in module.global_section_mut().unwrap().entries_mut() {
-        let init_expr = global.init_expr_mut();
-        *init_expr = InitExpr::new(vec![Instruction::I32Const(0x10000), Instruction::End]);
+/// Generate the the data segment and remove any empty segments.
+fn generate_data_section(section: SectionLimited<Data>, module: &mut Module) {
+    let mut segments = DataSection::new();
+    for segment in section
+        .into_iter()
+        .map(|segment| segment.unwrap())
+        .filter(|segment| segment.data.iter().any(|b| *b != 0))
+    {
+        match segment.kind {
+            DataKind::Active {
+                memory_index: 0, ..
+            } => assert!(segments.is_empty(), "expected only a single data segment"),
+            _ => panic!("expected an active data segment"),
+        }
+        segments.segment(DataSegment {
+            mode: DataSegmentMode::Active {
+                memory_index: 0, // Currently, the WASM spec allows only one memory per module
+                offset: &wasm_encoder::ConstExpr::i32_const(0),
+            },
+            data: segment.data.iter().cloned(),
+        });
     }
+    module.section(&segments);
+}
 
-    let linked = builder::module().with_module(module);
-
-    parity_wasm::serialize(linked.build()).expect("cannot serialize linked wasm")
+/// Set the stack pointer to 64k (this is the only global)
+fn generate_global_section(_section: SectionLimited<Global>, module: &mut Module) {
+    let mut globals = GlobalSection::new();
+    let global_type = GlobalType {
+        val_type: ValType::I32,
+        mutable: true,
+    };
+    globals.global(global_type, &ConstExpr::i32_const(0x10000));
+    module.section(&globals);
 }