Update to the new wasmparser and port to the new readers API.
The new wasmparser API provides dedicated reader types for each section type, which significantly simplifies the code. This also changes WasmError::from_binary_reader_error into a From trait so that we don't have to do .map_err(from_binary_reader_error) throughout the code.
This commit is contained in:
@@ -10,7 +10,7 @@ readme = "README.md"
|
|||||||
keywords = ["webassembly", "wasm"]
|
keywords = ["webassembly", "wasm"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmparser = { version = "0.19.1", default-features = false }
|
wasmparser = { version = "0.21.4", default-features = false }
|
||||||
cranelift-codegen = { path = "../codegen", version = "0.22.0", default-features = false }
|
cranelift-codegen = { path = "../codegen", version = "0.22.0", default-features = false }
|
||||||
cranelift-entity = { path = "../entity", version = "0.22.0", default-features = false }
|
cranelift-entity = { path = "../entity", version = "0.22.0", default-features = false }
|
||||||
cranelift-frontend = { path = "../frontend", version = "0.22.0", default-features = false }
|
cranelift-frontend = { path = "../frontend", version = "0.22.0", default-features = false }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
use cranelift_codegen::ir::{self, InstBuilder};
|
use cranelift_codegen::ir::{self, InstBuilder};
|
||||||
use cranelift_codegen::isa::TargetFrontendConfig;
|
use cranelift_codegen::isa::TargetFrontendConfig;
|
||||||
|
use std::convert::From;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use translation_utils::{
|
use translation_utils::{
|
||||||
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
|
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
|
||||||
@@ -62,9 +63,9 @@ pub enum WasmError {
|
|||||||
ImplLimitExceeded,
|
ImplLimitExceeded,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmError {
|
impl From<BinaryReaderError> for WasmError {
|
||||||
/// Convert from a `BinaryReaderError` to a `WasmError`.
|
/// Convert from a `BinaryReaderError` to a `WasmError`.
|
||||||
pub fn from_binary_reader_error(e: BinaryReaderError) -> Self {
|
fn from(e: BinaryReaderError) -> Self {
|
||||||
let BinaryReaderError { message, offset } = e;
|
let BinaryReaderError { message, offset } = e;
|
||||||
WasmError::InvalidWebAssembly { message, offset }
|
WasmError::InvalidWebAssembly { message, offset }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use cranelift_codegen::entity::EntityRef;
|
|||||||
use cranelift_codegen::ir::{self, Ebb, InstBuilder};
|
use cranelift_codegen::ir::{self, Ebb, InstBuilder};
|
||||||
use cranelift_codegen::timing;
|
use cranelift_codegen::timing;
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||||
use environ::{FuncEnvironment, ReturnMode, WasmError, WasmResult};
|
use environ::{FuncEnvironment, ReturnMode, WasmResult};
|
||||||
use state::TranslationState;
|
use state::TranslationState;
|
||||||
use wasmparser::{self, BinaryReader};
|
use wasmparser::{self, BinaryReader};
|
||||||
|
|
||||||
@@ -135,16 +135,12 @@ fn parse_local_decls(
|
|||||||
num_params: usize,
|
num_params: usize,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
let mut next_local = num_params;
|
let mut next_local = num_params;
|
||||||
let local_count = reader
|
let local_count = reader.read_local_count()?;
|
||||||
.read_local_count()
|
|
||||||
.map_err(WasmError::from_binary_reader_error)?;
|
|
||||||
|
|
||||||
let mut locals_total = 0;
|
let mut locals_total = 0;
|
||||||
for _ in 0..local_count {
|
for _ in 0..local_count {
|
||||||
builder.set_srcloc(cur_srcloc(reader));
|
builder.set_srcloc(cur_srcloc(reader));
|
||||||
let (count, ty) = reader
|
let (count, ty) = reader.read_local_decl(&mut locals_total)?;
|
||||||
.read_local_decl(&mut locals_total)
|
|
||||||
.map_err(WasmError::from_binary_reader_error)?;
|
|
||||||
declare_locals(builder, count, ty, &mut next_local);
|
declare_locals(builder, count, ty, &mut next_local);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,9 +191,7 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
|||||||
// Keep going until the final `End` operator which pops the outermost block.
|
// Keep going until the final `End` operator which pops the outermost block.
|
||||||
while !state.control_stack.is_empty() {
|
while !state.control_stack.is_empty() {
|
||||||
builder.set_srcloc(cur_srcloc(&reader));
|
builder.set_srcloc(cur_srcloc(&reader));
|
||||||
let op = reader
|
let op = reader.read_operator()?;
|
||||||
.read_operator()
|
|
||||||
.map_err(WasmError::from_binary_reader_error)?;
|
|
||||||
translate_operator(op, builder, state, environ)?;
|
translate_operator(op, builder, state, environ)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ mod std {
|
|||||||
|
|
||||||
pub use self::alloc::string;
|
pub use self::alloc::string;
|
||||||
pub use self::alloc::vec;
|
pub use self::alloc::vec;
|
||||||
|
pub use core::convert;
|
||||||
pub use core::fmt;
|
pub use core::fmt;
|
||||||
pub use core::option;
|
pub use core::option;
|
||||||
pub use core::{cmp, i32, str, u32};
|
pub use core::{cmp, i32, str, u32};
|
||||||
|
|||||||
@@ -1,119 +1,143 @@
|
|||||||
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
||||||
//! to deal with each part of it.
|
//! to deal with each part of it.
|
||||||
use cranelift_codegen::timing;
|
use cranelift_codegen::timing;
|
||||||
use environ::{ModuleEnvironment, WasmError, WasmResult};
|
use environ::{ModuleEnvironment, WasmResult};
|
||||||
use sections_translator::{
|
use sections_translator::{
|
||||||
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
|
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
|
||||||
parse_function_section, parse_function_signatures, parse_global_section, parse_import_section,
|
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
|
||||||
parse_memory_section, parse_start_section, parse_table_section,
|
parse_start_section, parse_table_section, parse_type_section,
|
||||||
};
|
};
|
||||||
use wasmparser::{Parser, ParserInput, ParserState, SectionCode, WasmDecoder};
|
use wasmparser::{ModuleReader, SectionCode};
|
||||||
|
|
||||||
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
|
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
|
||||||
/// [`Function`](../codegen/ir/function/struct.Function.html).
|
/// [`Function`](../codegen/ir/function/struct.Function.html).
|
||||||
/// Returns the functions and also the mappings for imported functions and signature between the
|
|
||||||
/// indexes in the wasm module and the indexes inside each functions.
|
|
||||||
pub fn translate_module<'data>(
|
pub fn translate_module<'data>(
|
||||||
data: &'data [u8],
|
data: &'data [u8],
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
let _tt = timing::wasm_translate_module();
|
let _tt = timing::wasm_translate_module();
|
||||||
let mut parser = Parser::new(data);
|
let mut reader = ModuleReader::new(data)?;
|
||||||
match *parser.read() {
|
|
||||||
ParserState::BeginWasm { .. } => {}
|
reader.skip_custom_sections()?;
|
||||||
ParserState::Error(e) => {
|
if reader.eof() {
|
||||||
return Err(WasmError::from_binary_reader_error(e));
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut section = reader.read()?;
|
||||||
|
|
||||||
|
if let SectionCode::Type = section.code {
|
||||||
|
let types = section.get_type_section_reader()?;
|
||||||
|
parse_type_section(types, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
ref s => panic!("modules should begin properly: {:?}", s),
|
section = reader.read()?;
|
||||||
}
|
}
|
||||||
let mut next_input = ParserInput::Default;
|
|
||||||
loop {
|
if let SectionCode::Import = section.code {
|
||||||
match *parser.read_with_input(next_input) {
|
let imports = section.get_import_section_reader()?;
|
||||||
ParserState::BeginSection {
|
parse_import_section(imports, environ)?;
|
||||||
code: SectionCode::Type,
|
|
||||||
..
|
reader.skip_custom_sections()?;
|
||||||
} => {
|
if reader.eof() {
|
||||||
parse_function_signatures(&mut parser, environ)?;
|
return Ok(());
|
||||||
next_input = ParserInput::Default;
|
}
|
||||||
}
|
section = reader.read()?;
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Import,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_import_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Function,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_function_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Table,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_table_section(&mut parser, environ)?;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Memory,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_memory_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Global,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_global_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Export,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_export_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Start,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_start_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Element,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_element_section(&mut parser, environ)?;
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Code,
|
|
||||||
..
|
|
||||||
} => parse_code_section(&mut parser, environ)?,
|
|
||||||
ParserState::EndSection => {
|
|
||||||
next_input = ParserInput::Default;
|
|
||||||
}
|
|
||||||
ParserState::EndWasm => return Ok(()),
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Data,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
parse_data_section(&mut parser, environ)?;
|
|
||||||
}
|
|
||||||
ParserState::BeginSection {
|
|
||||||
code: SectionCode::Custom { .. },
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// Ignore unknown custom sections.
|
|
||||||
next_input = ParserInput::SkipSection;
|
|
||||||
}
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
_ => panic!("wrong content in the preamble"),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Function = section.code {
|
||||||
|
let functions = section.get_function_section_reader()?;
|
||||||
|
parse_function_section(functions, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Table = section.code {
|
||||||
|
let tables = section.get_table_section_reader()?;
|
||||||
|
parse_table_section(tables, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Memory = section.code {
|
||||||
|
let memories = section.get_memory_section_reader()?;
|
||||||
|
parse_memory_section(memories, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Global = section.code {
|
||||||
|
let globals = section.get_global_section_reader()?;
|
||||||
|
parse_global_section(globals, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Export = section.code {
|
||||||
|
let exports = section.get_export_section_reader()?;
|
||||||
|
parse_export_section(exports, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Start = section.code {
|
||||||
|
let start = section.get_start_section_content()?;
|
||||||
|
parse_start_section(start, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Element = section.code {
|
||||||
|
let elements = section.get_element_section_reader()?;
|
||||||
|
parse_element_section(elements, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Code = section.code {
|
||||||
|
let code = section.get_code_section_reader()?;
|
||||||
|
parse_code_section(code, environ)?;
|
||||||
|
|
||||||
|
reader.skip_custom_sections()?;
|
||||||
|
if reader.eof() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
section = reader.read()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SectionCode::Data = section.code {
|
||||||
|
let data = section.get_data_section_reader()?;
|
||||||
|
parse_data_section(data, environ)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,32 +9,32 @@
|
|||||||
//! interpreted on the fly.
|
//! interpreted on the fly.
|
||||||
use cranelift_codegen::ir::{self, AbiParam, Signature};
|
use cranelift_codegen::ir::{self, AbiParam, Signature};
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use environ::{ModuleEnvironment, WasmError, WasmResult};
|
use environ::{ModuleEnvironment, WasmResult};
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use translation_utils::{
|
use translation_utils::{
|
||||||
type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex,
|
type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex,
|
||||||
Table, TableElementType, TableIndex,
|
Table, TableElementType, TableIndex,
|
||||||
};
|
};
|
||||||
use wasmparser;
|
|
||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser, ParserState,
|
self, CodeSectionReader, Data, DataSectionReader, Element, ElementSectionReader, Export,
|
||||||
WasmDecoder,
|
ExportSectionReader, ExternalKind, FuncType, FunctionSectionReader, GlobalSectionReader,
|
||||||
|
GlobalType, Import, ImportSectionEntryType, ImportSectionReader, MemorySectionReader,
|
||||||
|
MemoryType, Operator, TableSectionReader, TypeSectionReader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Reads the Type Section of the wasm module and returns the corresponding function signatures.
|
/// Parses the Type section of the wasm module.
|
||||||
pub fn parse_function_signatures(
|
pub fn parse_type_section(
|
||||||
parser: &mut Parser,
|
types: TypeSectionReader,
|
||||||
environ: &mut ModuleEnvironment,
|
environ: &mut ModuleEnvironment,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
loop {
|
for entry in types {
|
||||||
match *parser.read() {
|
match entry? {
|
||||||
ParserState::EndSection => break,
|
FuncType {
|
||||||
ParserState::TypeSectionEntry(FuncType {
|
|
||||||
form: wasmparser::Type::Func,
|
form: wasmparser::Type::Func,
|
||||||
ref params,
|
ref params,
|
||||||
ref returns,
|
ref returns,
|
||||||
}) => {
|
} => {
|
||||||
let mut sig = Signature::new(environ.target_config().default_call_conv);
|
let mut sig = Signature::new(environ.target_config().default_call_conv);
|
||||||
sig.params.extend(params.iter().map(|ty| {
|
sig.params.extend(params.iter().map(|ty| {
|
||||||
let cret_arg: ir::Type = type_to_type(*ty)
|
let cret_arg: ir::Type = type_to_type(*ty)
|
||||||
@@ -48,24 +48,23 @@ pub fn parse_function_signatures(
|
|||||||
}));
|
}));
|
||||||
environ.declare_signature(&sig);
|
environ.declare_signature(&sig);
|
||||||
}
|
}
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
ref s => panic!("unsupported type: {:?}", s),
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the imports from the imports section of the binary.
|
/// Parses the Import section of the wasm module.
|
||||||
pub fn parse_import_section<'data>(
|
pub fn parse_import_section<'data>(
|
||||||
parser: &mut Parser<'data>,
|
imports: ImportSectionReader<'data>,
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
loop {
|
for entry in imports {
|
||||||
match *parser.read() {
|
match entry? {
|
||||||
ParserState::ImportSectionEntry {
|
Import {
|
||||||
ty: ImportSectionEntryType::Function(sig),
|
|
||||||
module,
|
module,
|
||||||
field,
|
field,
|
||||||
|
ty: ImportSectionEntryType::Function(sig),
|
||||||
} => {
|
} => {
|
||||||
// The input has already been validated, so we should be able to
|
// The input has already been validated, so we should be able to
|
||||||
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
||||||
@@ -78,7 +77,7 @@ pub fn parse_import_section<'data>(
|
|||||||
field_name,
|
field_name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ParserState::ImportSectionEntry {
|
Import {
|
||||||
ty:
|
ty:
|
||||||
ImportSectionEntryType::Memory(MemoryType {
|
ImportSectionEntryType::Memory(MemoryType {
|
||||||
limits: ref memlimits,
|
limits: ref memlimits,
|
||||||
@@ -92,7 +91,7 @@ pub fn parse_import_section<'data>(
|
|||||||
shared,
|
shared,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ParserState::ImportSectionEntry {
|
Import {
|
||||||
ty: ImportSectionEntryType::Global(ref ty),
|
ty: ImportSectionEntryType::Global(ref ty),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
@@ -102,7 +101,7 @@ pub fn parse_import_section<'data>(
|
|||||||
initializer: GlobalInit::Import(),
|
initializer: GlobalInit::Import(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ParserState::ImportSectionEntry {
|
Import {
|
||||||
ty: ImportSectionEntryType::Table(ref tab),
|
ty: ImportSectionEntryType::Table(ref tab),
|
||||||
..
|
..
|
||||||
} => environ.declare_table(Table {
|
} => environ.declare_table(Table {
|
||||||
@@ -113,333 +112,203 @@ pub fn parse_import_section<'data>(
|
|||||||
size: tab.limits.initial as usize,
|
size: tab.limits.initial as usize,
|
||||||
maximum: tab.limits.maximum.map(|x| x as usize),
|
maximum: tab.limits.maximum.map(|x| x as usize),
|
||||||
}),
|
}),
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the correspondences between functions and signatures from the function section
|
|
||||||
pub fn parse_function_section(
|
|
||||||
parser: &mut Parser,
|
|
||||||
environ: &mut ModuleEnvironment,
|
|
||||||
) -> WasmResult<()> {
|
|
||||||
loop {
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::FunctionSectionEntry(sigindex) => {
|
|
||||||
environ.declare_func_type(SignatureIndex::new(sigindex as usize));
|
|
||||||
}
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the names of the functions from the export section
|
|
||||||
pub fn parse_export_section<'data>(
|
|
||||||
parser: &mut Parser<'data>,
|
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
|
||||||
) -> WasmResult<()> {
|
|
||||||
loop {
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::ExportSectionEntry {
|
|
||||||
field,
|
|
||||||
ref kind,
|
|
||||||
index,
|
|
||||||
} => {
|
|
||||||
// The input has already been validated, so we should be able to
|
|
||||||
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
|
||||||
// becomes a concern here.
|
|
||||||
let name = from_utf8(field).unwrap();
|
|
||||||
let index = index as usize;
|
|
||||||
match *kind {
|
|
||||||
ExternalKind::Function => {
|
|
||||||
environ.declare_func_export(FuncIndex::new(index), name)
|
|
||||||
}
|
|
||||||
ExternalKind::Table => {
|
|
||||||
environ.declare_table_export(TableIndex::new(index), name)
|
|
||||||
}
|
|
||||||
ExternalKind::Memory => {
|
|
||||||
environ.declare_memory_export(MemoryIndex::new(index), name)
|
|
||||||
}
|
|
||||||
ExternalKind::Global => {
|
|
||||||
environ.declare_global_export(GlobalIndex::new(index), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the start function index from the start section
|
|
||||||
pub fn parse_start_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
|
||||||
loop {
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::StartSectionEntry(index) => {
|
|
||||||
environ.declare_start_func(FuncIndex::new(index as usize));
|
|
||||||
}
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the size and maximum fields of memories from the memory section
|
|
||||||
pub fn parse_memory_section(
|
|
||||||
parser: &mut Parser,
|
|
||||||
environ: &mut ModuleEnvironment,
|
|
||||||
) -> WasmResult<()> {
|
|
||||||
loop {
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::MemorySectionEntry(ref ty) => {
|
|
||||||
environ.declare_memory(Memory {
|
|
||||||
pages_count: ty.limits.initial as usize,
|
|
||||||
maximum: ty.limits.maximum.map(|x| x as usize),
|
|
||||||
shared: ty.shared,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the size and maximum fields of memories from the memory section
|
|
||||||
pub fn parse_global_section(
|
|
||||||
parser: &mut Parser,
|
|
||||||
environ: &mut ModuleEnvironment,
|
|
||||||
) -> WasmResult<()> {
|
|
||||||
loop {
|
|
||||||
let (content_type, mutability) = match *parser.read() {
|
|
||||||
ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable),
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::BeginInitExpressionBody => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
}
|
}
|
||||||
let initializer = match *parser.read() {
|
}
|
||||||
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
Ok(())
|
||||||
GlobalInit::I32Const(value)
|
}
|
||||||
}
|
|
||||||
ParserState::InitExpressionOperator(Operator::I64Const { value }) => {
|
/// Parses the Function section of the wasm module.
|
||||||
GlobalInit::I64Const(value)
|
pub fn parse_function_section(
|
||||||
}
|
functions: FunctionSectionReader,
|
||||||
ParserState::InitExpressionOperator(Operator::F32Const { value }) => {
|
environ: &mut ModuleEnvironment,
|
||||||
GlobalInit::F32Const(value.bits())
|
) -> WasmResult<()> {
|
||||||
}
|
for entry in functions {
|
||||||
ParserState::InitExpressionOperator(Operator::F64Const { value }) => {
|
let sigindex = entry?;
|
||||||
GlobalInit::F64Const(value.bits())
|
environ.declare_func_type(SignatureIndex::new(sigindex as usize));
|
||||||
}
|
}
|
||||||
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the Table section of the wasm module.
|
||||||
|
pub fn parse_table_section(
|
||||||
|
tables: TableSectionReader,
|
||||||
|
environ: &mut ModuleEnvironment,
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
for entry in tables {
|
||||||
|
let table = entry?;
|
||||||
|
environ.declare_table(Table {
|
||||||
|
ty: match type_to_type(table.element_type) {
|
||||||
|
Ok(t) => TableElementType::Val(t),
|
||||||
|
Err(()) => TableElementType::Func(),
|
||||||
|
},
|
||||||
|
size: table.limits.initial as usize,
|
||||||
|
maximum: table.limits.maximum.map(|x| x as usize),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the Memory section of the wasm module.
|
||||||
|
pub fn parse_memory_section(
|
||||||
|
memories: MemorySectionReader,
|
||||||
|
environ: &mut ModuleEnvironment,
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
for entry in memories {
|
||||||
|
let memory = entry?;
|
||||||
|
environ.declare_memory(Memory {
|
||||||
|
pages_count: memory.limits.initial as usize,
|
||||||
|
maximum: memory.limits.maximum.map(|x| x as usize),
|
||||||
|
shared: memory.shared,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the Global section of the wasm module.
|
||||||
|
pub fn parse_global_section(
|
||||||
|
globals: GlobalSectionReader,
|
||||||
|
environ: &mut ModuleEnvironment,
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
for entry in globals {
|
||||||
|
let wasmparser::Global {
|
||||||
|
ty: GlobalType {
|
||||||
|
content_type,
|
||||||
|
mutable,
|
||||||
|
},
|
||||||
|
init_expr,
|
||||||
|
} = entry?;
|
||||||
|
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||||
|
let initializer = match init_expr_reader.read_operator()? {
|
||||||
|
Operator::I32Const { value } => GlobalInit::I32Const(value),
|
||||||
|
Operator::I64Const { value } => GlobalInit::I64Const(value),
|
||||||
|
Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
|
||||||
|
Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
|
||||||
|
Operator::GetGlobal { global_index } => {
|
||||||
GlobalInit::GlobalRef(GlobalIndex::new(global_index as usize))
|
GlobalInit::GlobalRef(GlobalIndex::new(global_index as usize))
|
||||||
}
|
}
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
ref s => panic!("unsupported init expr in global section: {:?}", s),
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
};
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndInitExpressionBody => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
}
|
|
||||||
let global = Global {
|
let global = Global {
|
||||||
ty: type_to_type(content_type).unwrap(),
|
ty: type_to_type(content_type).unwrap(),
|
||||||
mutability,
|
mutability: mutable,
|
||||||
initializer,
|
initializer,
|
||||||
};
|
};
|
||||||
environ.declare_global(global);
|
environ.declare_global(global);
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndGlobalSectionEntry => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_data_section<'data>(
|
/// Parses the Export section of the wasm module.
|
||||||
parser: &mut Parser<'data>,
|
pub fn parse_export_section<'data>(
|
||||||
|
exports: ExportSectionReader<'data>,
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
loop {
|
for entry in exports {
|
||||||
let memory_index = match *parser.read() {
|
let Export {
|
||||||
ParserState::BeginDataSectionEntry(memory_index) => memory_index,
|
field,
|
||||||
ParserState::EndSection => break,
|
ref kind,
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
index,
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
} = entry?;
|
||||||
};
|
|
||||||
match *parser.read() {
|
// The input has already been validated, so we should be able to
|
||||||
ParserState::BeginInitExpressionBody => (),
|
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
// becomes a concern here.
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
let name = from_utf8(field).unwrap();
|
||||||
};
|
let index = index as usize;
|
||||||
let (base, offset) = match *parser.read() {
|
match *kind {
|
||||||
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), name),
|
||||||
(None, value as u32 as usize)
|
ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), name),
|
||||||
}
|
ExternalKind::Memory => environ.declare_memory_export(MemoryIndex::new(index), name),
|
||||||
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
ExternalKind::Global => environ.declare_global_export(GlobalIndex::new(index), name),
|
||||||
match environ
|
|
||||||
.get_global(GlobalIndex::new(global_index as usize))
|
|
||||||
.initializer
|
|
||||||
{
|
|
||||||
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
|
||||||
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
|
||||||
_ => panic!("should not happen"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndInitExpressionBody => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::BeginDataSectionEntryBody(_) => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
let mut running_offset = offset;
|
|
||||||
loop {
|
|
||||||
let data = match *parser.read() {
|
|
||||||
ParserState::DataSectionEntryBodyChunk(data) => data,
|
|
||||||
ParserState::EndDataSectionEntryBody => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
environ.declare_data_initialization(
|
|
||||||
MemoryIndex::new(memory_index as usize),
|
|
||||||
base,
|
|
||||||
running_offset,
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
running_offset += data.len();
|
|
||||||
}
|
}
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndDataSectionEntry => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the tables from the table section
|
/// Parses the Start section of the wasm module.
|
||||||
pub fn parse_table_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
||||||
loop {
|
environ.declare_start_func(FuncIndex::new(index as usize));
|
||||||
match *parser.read() {
|
|
||||||
ParserState::TableSectionEntry(ref table) => environ.declare_table(Table {
|
|
||||||
ty: match type_to_type(table.element_type) {
|
|
||||||
Ok(t) => TableElementType::Val(t),
|
|
||||||
Err(()) => TableElementType::Func(),
|
|
||||||
},
|
|
||||||
size: table.limits.initial as usize,
|
|
||||||
maximum: table.limits.maximum.map(|x| x as usize),
|
|
||||||
}),
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the elements from the element section
|
/// Parses the Element section of the wasm module.
|
||||||
pub fn parse_element_section(
|
pub fn parse_element_section<'data>(
|
||||||
parser: &mut Parser,
|
elements: ElementSectionReader<'data>,
|
||||||
environ: &mut ModuleEnvironment,
|
environ: &mut ModuleEnvironment,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
loop {
|
for entry in elements {
|
||||||
let table_index = match *parser.read() {
|
let Element {
|
||||||
ParserState::BeginElementSectionEntry(table_index) => {
|
table_index,
|
||||||
TableIndex::new(table_index as usize)
|
init_expr,
|
||||||
}
|
items,
|
||||||
ParserState::EndSection => break,
|
} = entry?;
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
let (base, offset) = match init_expr_reader.read_operator()? {
|
||||||
};
|
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||||
match *parser.read() {
|
Operator::GetGlobal { global_index } => match environ
|
||||||
ParserState::BeginInitExpressionBody => (),
|
.get_global(GlobalIndex::new(global_index as usize))
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
.initializer
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
{
|
||||||
};
|
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
||||||
let (base, offset) = match *parser.read() {
|
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
||||||
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
_ => panic!("should not happen"),
|
||||||
(None, value as u32 as usize)
|
},
|
||||||
}
|
ref s => panic!("unsupported init expr in element section: {:?}", s),
|
||||||
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
|
||||||
match environ
|
|
||||||
.get_global(GlobalIndex::new(global_index as usize))
|
|
||||||
.initializer
|
|
||||||
{
|
|
||||||
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
|
||||||
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
|
||||||
_ => panic!("should not happen"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndInitExpressionBody => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::ElementSectionEntryBody(ref elements) => {
|
|
||||||
let elems: Vec<FuncIndex> = elements
|
|
||||||
.iter()
|
|
||||||
.map(|&x| FuncIndex::new(x as usize))
|
|
||||||
.collect();
|
|
||||||
environ.declare_table_elements(table_index, base, offset, elems)
|
|
||||||
}
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
|
||||||
match *parser.read() {
|
|
||||||
ParserState::EndElementSectionEntry => (),
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("unexpected section content: {:?}", s),
|
|
||||||
};
|
};
|
||||||
|
let items_reader = items.get_items_reader()?;
|
||||||
|
let mut elems = Vec::new();
|
||||||
|
for item in items_reader {
|
||||||
|
let x = item?;
|
||||||
|
elems.push(FuncIndex::new(x as usize));
|
||||||
|
}
|
||||||
|
environ.declare_table_elements(TableIndex::new(table_index as usize), base, offset, elems)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses every function body in the code section and defines the corresponding function.
|
/// Parses the Code section of the wasm module.
|
||||||
pub fn parse_code_section<'data>(
|
pub fn parse_code_section<'data>(
|
||||||
parser: &mut Parser<'data>,
|
code: CodeSectionReader<'data>,
|
||||||
environ: &mut ModuleEnvironment<'data>,
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
loop {
|
for body in code {
|
||||||
match *parser.read() {
|
let mut reader = body?.get_binary_reader();
|
||||||
ParserState::BeginFunctionBody { .. } => {}
|
|
||||||
ParserState::EndSection => break,
|
|
||||||
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
||||||
ref s => panic!("wrong content in code section: {:?}", s),
|
|
||||||
}
|
|
||||||
let mut reader = parser.create_binary_reader();
|
|
||||||
let size = reader.bytes_remaining();
|
let size = reader.bytes_remaining();
|
||||||
environ.define_function_body(
|
environ.define_function_body(reader.read_bytes(size)?)?;
|
||||||
reader
|
}
|
||||||
.read_bytes(size)
|
Ok(())
|
||||||
.map_err(WasmError::from_binary_reader_error)?,
|
}
|
||||||
)?;
|
|
||||||
|
/// Parses the Data section of the wasm module.
|
||||||
|
pub fn parse_data_section<'data>(
|
||||||
|
data: DataSectionReader<'data>,
|
||||||
|
environ: &mut ModuleEnvironment<'data>,
|
||||||
|
) -> WasmResult<()> {
|
||||||
|
for entry in data {
|
||||||
|
let Data {
|
||||||
|
memory_index,
|
||||||
|
init_expr,
|
||||||
|
data,
|
||||||
|
} = entry?;
|
||||||
|
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||||
|
let (base, offset) = match init_expr_reader.read_operator()? {
|
||||||
|
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||||
|
Operator::GetGlobal { global_index } => match environ
|
||||||
|
.get_global(GlobalIndex::new(global_index as usize))
|
||||||
|
.initializer
|
||||||
|
{
|
||||||
|
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
||||||
|
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
||||||
|
_ => panic!("should not happen"),
|
||||||
|
},
|
||||||
|
ref s => panic!("unsupported init expr in data section: {:?}", s),
|
||||||
|
};
|
||||||
|
environ.declare_data_initialization(
|
||||||
|
MemoryIndex::new(memory_index as usize),
|
||||||
|
base,
|
||||||
|
offset,
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user