Update wasmparser to 0.59.0 (#2013)

This commit is intended to update wasmparser to 0.59.0. This primarily
includes bytecodealliance/wasm-tools#40 which is a large update to how
parsing and validation works. The impact on Wasmtime is pretty small at
this time, but over time I'd like to refactor the internals here to lean
more heavily on that upstream wasmparser refactoring.

For now, though, the intention is to get on the train of wasmparser's
latest `main` branch to ensure we get bug fixes and such.

As part of this update a few other crates and such were updated. This is
primarily to handle the new encoding of `ref.is_null` where the type is
not part of the instruction encoding any more.
This commit is contained in:
Alex Crichton
2020-07-13 16:22:41 -05:00
committed by GitHub
parent 9bafb173a0
commit 1000f21338
23 changed files with 205 additions and 316 deletions

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
gimli = "0.21.0"
wasmparser = "0.58.0"
wasmparser = "0.59.0"
object = { version = "0.20", default-features = false, features = ["read", "write"] }
wasmtime-environ = { path = "../environ", version = "0.18.0" }
target-lexicon = { version = "0.10.0", default-features = false }

View File

@@ -6,7 +6,7 @@ use gimli::{
};
use std::collections::HashMap;
use std::path::PathBuf;
use wasmparser::{self, ModuleReader, SectionCode, TypeDef};
use wasmparser::{self, NameSectionReader, Parser, Payload, TypeDef};
trait Reader: gimli::Reader<Offset = usize, Endian = LittleEndian> {}
@@ -155,7 +155,6 @@ fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Resul
}
pub fn read_debuginfo(data: &[u8]) -> Result<DebugInfoData> {
let mut reader = ModuleReader::new(data)?;
let mut sections = HashMap::new();
let mut name_section = None;
let mut code_section_offset = 0;
@@ -165,26 +164,25 @@ pub fn read_debuginfo(data: &[u8]) -> Result<DebugInfoData> {
let mut func_params_refs: Vec<usize> = Vec::new();
let mut func_locals: Vec<Box<[(u32, WasmType)]>> = Vec::new();
while !reader.eof() {
let section = reader.read()?;
match section.code {
SectionCode::Custom { name, .. } => {
for payload in Parser::new(0).parse_all(data) {
match payload? {
Payload::CustomSection {
name,
data,
data_offset,
} => {
if name.starts_with(".debug_") {
let mut reader = section.get_binary_reader();
let len = reader.bytes_remaining();
sections.insert(name, reader.read_bytes(len)?);
}
if name == "name" {
if let Ok(reader) = section.get_name_section_reader() {
sections.insert(name, data);
} else if name == "name" {
if let Ok(reader) = NameSectionReader::new(data, data_offset) {
if let Ok(section) = read_name_section(reader) {
name_section = Some(section);
}
}
}
}
SectionCode::Type => {
signatures_params = section
.get_type_section_reader()?
Payload::TypeSection(s) => {
signatures_params = s
.into_iter()
.map(|ft| {
if let Ok(TypeDef::Func(ft)) = ft {
@@ -195,33 +193,29 @@ pub fn read_debuginfo(data: &[u8]) -> Result<DebugInfoData> {
})
.collect::<Result<Vec<_>>>()?;
}
SectionCode::Import => {
for i in section.get_import_section_reader()? {
Payload::ImportSection(s) => {
for i in s {
if let wasmparser::ImportSectionEntryType::Function(_) = i?.ty {
imported_func_count += 1;
}
}
}
SectionCode::Function => {
func_params_refs = section
.get_function_section_reader()?
Payload::FunctionSection(s) => {
func_params_refs = s
.into_iter()
.map(|index| Ok(index? as usize))
.collect::<Result<Vec<_>>>()?;
}
SectionCode::Code => {
code_section_offset = section.range().start as u64;
func_locals = section
.get_code_section_reader()?
Payload::CodeSectionStart { range, .. } => {
code_section_offset = range.start as u64;
}
Payload::CodeSectionEntry(body) => {
let locals = body.get_locals_reader()?;
let locals = locals
.into_iter()
.map(|body| {
let locals = body?.get_locals_reader()?;
Ok(locals
.into_iter()
.collect::<Result<Vec<_>, _>>()?
.into_boxed_slice())
})
.collect::<Result<Vec<_>>>()?;
.collect::<Result<Vec<_>, _>>()?
.into_boxed_slice();
func_locals.push(locals);
}
_ => (),
}

View File

@@ -17,7 +17,7 @@ cranelift-codegen = { path = "../../cranelift/codegen", version = "0.65.0", feat
cranelift-entity = { path = "../../cranelift/entity", version = "0.65.0", features = ["enable-serde"] }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.65.0" }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.65.0", features = ["enable-serde"] }
wasmparser = "0.58.0"
wasmparser = "0.59.0"
lightbeam = { path = "../lightbeam", optional = true, version = "0.18.0" }
indexmap = { version = "1.0.2", features = ["serde-1"] }
rayon = { version = "1.2.1", optional = true }

View File

@@ -13,8 +13,8 @@ binaryen = { version = "0.10.0", optional = true }
env_logger = "0.7.1"
log = "0.4.8"
rayon = "1.2.1"
wasmparser = "0.58.0"
wasmprinter = "0.2.5"
wasmparser = "0.59.0"
wasmprinter = "0.2.6"
wasmtime = { path = "../wasmtime" }
wasmtime-wast = { path = "../wast" }

View File

@@ -213,14 +213,12 @@ fn arbitrary_config(
/// it'll free up new slots to start making new instances.
fn predict_rss(wasm: &[u8]) -> Result<usize> {
let mut prediction = 0;
let mut reader = ModuleReader::new(wasm)?;
while !reader.eof() {
let section = reader.read()?;
match section.code {
for payload in Parser::new(0).parse_all(wasm) {
match payload? {
// For each declared memory we'll have to map that all in, so add in
// the minimum amount of memory to our predicted rss.
SectionCode::Memory => {
for entry in section.get_memory_section_reader()? {
Payload::MemorySection(s) => {
for entry in s {
let initial = entry?.limits.initial as usize;
prediction += initial * 64 * 1024;
}
@@ -228,8 +226,8 @@ fn predict_rss(wasm: &[u8]) -> Result<usize> {
// We'll need to allocate tables and space for table elements, and
// currently this is 3 pointers per table entry.
SectionCode::Table => {
for entry in section.get_table_section_reader()? {
Payload::TableSection(s) => {
for entry in s {
let initial = entry?.limits.initial as usize;
prediction += initial * 3 * mem::size_of::<usize>();
}

View File

@@ -25,7 +25,7 @@ wasmtime-obj = { path = "../obj", version = "0.18.0" }
region = "2.1.0"
thiserror = "1.0.4"
target-lexicon = { version = "0.10.0", default-features = false }
wasmparser = "0.58.0"
wasmparser = "0.59.0"
more-asserts = "0.2.1"
anyhow = "1.0"
cfg-if = "0.1.9"

View File

@@ -24,7 +24,7 @@ smallvec = "1.0.0"
staticvec = "0.10"
thiserror = "1.0.9"
typemap = "0.3"
wasmparser = "0.58.0"
wasmparser = "0.59.0"
[dev-dependencies]
lazy_static = "1.2"

View File

@@ -2130,7 +2130,7 @@ where
WasmOperator::RefNull { ty: _ } => {
return Err(Error::Microwasm("RefNull unimplemented".into()))
}
WasmOperator::RefIsNull { ty: _ } => {
WasmOperator::RefIsNull => {
return Err(Error::Microwasm("RefIsNull unimplemented".into()))
}
WasmOperator::I32Eqz => one(Operator::Eqz(Size::_32)),

View File

@@ -10,7 +10,7 @@ use memoffset::offset_of;
use std::{convert::TryInto, mem};
use thiserror::Error;
use wasmparser::{FuncType, MemoryType, ModuleReader, SectionCode, Type};
use wasmparser::{FuncType, MemoryType, Parser, Payload, Type};
pub trait AsValueType {
const TYPE: Type;
@@ -512,150 +512,58 @@ pub fn translate(data: &[u8]) -> Result<ExecutableModule, Error> {
/// Translate from a slice of bytes holding a wasm module.
pub fn translate_only(data: &[u8]) -> Result<TranslatedModule, Error> {
let mut reader = ModuleReader::new(data)?;
let mut output = TranslatedModule::default();
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
let mut section = reader.read()?;
if let SectionCode::Type = section.code {
let types_reader = section.get_type_section_reader()?;
output.ctx.types = translate_sections::type_(types_reader)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Import = section.code {
let imports = section.get_import_section_reader()?;
translate_sections::import(imports)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Function = section.code {
let functions = section.get_function_section_reader()?;
output.ctx.func_ty_indices = translate_sections::function(functions)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Table = section.code {
let tables = section.get_table_section_reader()?;
translate_sections::table(tables)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Memory = section.code {
let memories = section.get_memory_section_reader()?;
let mem = translate_sections::memory(memories)?;
if mem.len() > 1 {
return Err(Error::Input(
"Multiple memory sections not yet implemented".to_string(),
));
}
if !mem.is_empty() {
let mem = mem[0];
if Some(mem.limits.initial) != mem.limits.maximum {
return Err(Error::Input(
"Custom memory limits not supported in lightbeam".to_string(),
));
for payload in Parser::new(0).parse_all(data) {
match payload? {
Payload::TypeSection(s) => output.ctx.types = translate_sections::type_(s)?,
Payload::ImportSection(s) => translate_sections::import(s)?,
Payload::FunctionSection(s) => {
output.ctx.func_ty_indices = translate_sections::function(s)?;
}
output.memory = Some(mem);
Payload::TableSection(s) => {
translate_sections::table(s)?;
}
Payload::MemorySection(s) => {
let mem = translate_sections::memory(s)?;
if mem.len() > 1 {
return Err(Error::Input(
"Multiple memory sections not yet implemented".to_string(),
));
}
if !mem.is_empty() {
let mem = mem[0];
if Some(mem.limits.initial) != mem.limits.maximum {
return Err(Error::Input(
"Custom memory limits not supported in lightbeam".to_string(),
));
}
output.memory = Some(mem);
}
}
Payload::GlobalSection(s) => {
translate_sections::global(s)?;
}
Payload::ExportSection(s) => {
translate_sections::export(s)?;
}
Payload::StartSection { func, .. } => {
translate_sections::start(func)?;
}
Payload::ElementSection(s) => {
translate_sections::element(s)?;
}
Payload::DataSection(s) => {
translate_sections::data(s)?;
}
Payload::CodeSectionStart { .. }
| Payload::CustomSection { .. }
| Payload::Version { .. } => {}
other => unimplemented!("can't translate {:?}", other),
}
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Global = section.code {
let globals = section.get_global_section_reader()?;
translate_sections::global(globals)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Export = section.code {
let exports = section.get_export_section_reader()?;
translate_sections::export(exports)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Start = section.code {
let start = section.get_start_section_content()?;
translate_sections::start(start)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Element = section.code {
let elements = section.get_element_section_reader()?;
translate_sections::element(elements)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Code = section.code {
let code = section.get_code_section_reader()?;
output.translated_code_section = Some(translate_sections::code(code, &output.ctx)?);
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Data = section.code {
let data = section.get_data_section_reader()?;
translate_sections::data(data)?;
}
if !reader.eof() {
return Err(Error::Input(
"Unexpected data found after the end of the module".to_string(),
));
}
Ok(output)

View File

@@ -108,6 +108,7 @@ impl binemit::RelocSink for UnimplementedRelocSink {
}
/// Parses the Code section of the wasm module.
#[allow(dead_code)]
pub fn code(
_code: CodeSectionReader,
_translation_ctx: &SimpleContext,

View File

@@ -14,7 +14,7 @@ wasmtime-runtime = { path = "../runtime", version = "0.18.0" }
wasmtime-environ = { path = "../environ", version = "0.18.0" }
wasmtime-jit = { path = "../jit", version = "0.18.0" }
wasmtime-profiling = { path = "../profiling", version = "0.18.0" }
wasmparser = "0.58.0"
wasmparser = "0.59.0"
target-lexicon = { version = "0.10.0", default-features = false }
anyhow = "1.0.19"
region = "2.2.0"

View File

@@ -66,7 +66,7 @@ fn instantiate(
let instance = store.add_instance(instance);
instance
.initialize(
config.validating_config.operator_config.enable_bulk_memory,
config.wasm_bulk_memory,
&compiled_module.data_initializers(),
)
.map_err(|e| -> Error {

View File

@@ -1,10 +1,9 @@
use crate::frame_info::GlobalFrameInfoRegistration;
use crate::runtime::Engine;
use crate::types::{EntityType, ExportType, ExternType, ImportType};
use anyhow::{Error, Result};
use anyhow::Result;
use std::path::Path;
use std::sync::{Arc, Mutex};
use wasmparser::validate;
use wasmtime_jit::CompiledModule;
/// A compiled WebAssembly module, ready to be instantiated.
@@ -296,8 +295,8 @@ impl Module {
///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
let config = engine.config().validating_config.clone();
validate(binary, Some(config)).map_err(Error::new)
engine.config().validator().validate_all(binary)?;
Ok(())
}
unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result<Self> {

View File

@@ -10,7 +10,7 @@ use std::hash::{Hash, Hasher};
use std::path::Path;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
use wasmparser::Validator;
use wasmtime_environ::settings::{self, Configurable, SetError};
use wasmtime_environ::{ir, isa, isa::TargetIsa, wasm, CacheConfig, Tunables};
use wasmtime_jit::{native, CompilationStrategy, Compiler};
@@ -34,13 +34,17 @@ use wasmtime_runtime::{
pub struct Config {
pub(crate) flags: settings::Builder,
pub(crate) isa_flags: isa::Builder,
pub(crate) validating_config: ValidatingParserConfig,
pub(crate) tunables: Tunables,
pub(crate) strategy: CompilationStrategy,
pub(crate) cache_config: CacheConfig,
pub(crate) profiler: Arc<dyn ProfilingAgent>,
pub(crate) memory_creator: Option<MemoryCreatorProxy>,
pub(crate) max_wasm_stack: usize,
wasm_threads: bool,
wasm_reference_types: bool,
pub(crate) wasm_bulk_memory: bool,
wasm_simd: bool,
wasm_multi_value: bool,
}
impl Config {
@@ -81,17 +85,6 @@ impl Config {
Config {
tunables,
validating_config: ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: false,
enable_reference_types: false,
enable_bulk_memory: false,
enable_simd: false,
enable_multi_value: true,
enable_tail_call: false,
enable_module_linking: false,
},
},
flags,
isa_flags: native::builder(),
strategy: CompilationStrategy::Auto,
@@ -99,6 +92,11 @@ impl Config {
profiler: Arc::new(NullProfilerAgent),
memory_creator: None,
max_wasm_stack: 1 << 20,
wasm_threads: false,
wasm_reference_types: false,
wasm_bulk_memory: false,
wasm_simd: false,
wasm_multi_value: true,
}
}
@@ -163,7 +161,7 @@ impl Config {
///
/// [threads]: https://github.com/webassembly/threads
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.validating_config.operator_config.enable_threads = enable;
self.wasm_threads = enable;
// The threads proposal depends on the bulk memory proposal
if enable {
self.wasm_bulk_memory(true);
@@ -193,9 +191,7 @@ impl Config {
///
/// [proposal]: https://github.com/webassembly/reference-types
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.validating_config
.operator_config
.enable_reference_types = enable;
self.wasm_reference_types = enable;
self.flags
.set("enable_safepoints", if enable { "true" } else { "false" })
@@ -230,7 +226,7 @@ impl Config {
///
/// [proposal]: https://github.com/webassembly/simd
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.validating_config.operator_config.enable_simd = enable;
self.wasm_simd = enable;
let val = if enable { "true" } else { "false" };
self.flags
.set("enable_simd", val)
@@ -254,7 +250,7 @@ impl Config {
///
/// [proposal]: https://github.com/webassembly/bulk-memory-operations
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.validating_config.operator_config.enable_bulk_memory = enable;
self.wasm_bulk_memory = enable;
self
}
@@ -268,7 +264,7 @@ impl Config {
///
/// [proposal]: https://github.com/webassembly/multi-value
pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
self.validating_config.operator_config.enable_multi_value = enable;
self.wasm_multi_value = enable;
self
}
@@ -613,6 +609,16 @@ impl Config {
.finish(settings::Flags::new(self.flags.clone()))
}
pub(crate) fn validator(&self) -> Validator {
let mut ret = Validator::new();
ret.wasm_threads(self.wasm_threads)
.wasm_bulk_memory(self.wasm_bulk_memory)
.wasm_multi_value(self.wasm_multi_value)
.wasm_reference_types(self.wasm_reference_types)
.wasm_simd(self.wasm_simd);
return ret;
}
fn build_compiler(&self) -> Compiler {
let isa = self.target_isa();
Compiler::new(
@@ -640,15 +646,14 @@ impl Default for Config {
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let features = &self.validating_config.operator_config;
f.debug_struct("Config")
.field("debug_info", &self.tunables.debug_info)
.field("strategy", &self.strategy)
.field("wasm_threads", &features.enable_threads)
.field("wasm_reference_types", &features.enable_reference_types)
.field("wasm_bulk_memory", &features.enable_bulk_memory)
.field("wasm_simd", &features.enable_simd)
.field("wasm_multi_value", &features.enable_multi_value)
.field("wasm_threads", &self.wasm_threads)
.field("wasm_reference_types", &self.wasm_reference_types)
.field("wasm_bulk_memory", &self.wasm_bulk_memory)
.field("wasm_simd", &self.wasm_simd)
.field("wasm_multi_value", &self.wasm_multi_value)
.field(
"flags",
&settings::Flags::new(self.flags.clone()).to_string(),

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0.19"
wasmtime = { path = "../wasmtime", version = "0.18.0", default-features = false }
wast = "18.0.0"
wast = "21.0.0"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -6,7 +6,7 @@ use wasmtime::*;
use wast::Wat;
use wast::{
parser::{self, ParseBuffer},
RefType,
HeapType,
};
/// Translate from a `script::Value` to a `RuntimeValue`.
@@ -22,8 +22,8 @@ fn runtime_value(v: &wast::Expression<'_>) -> Result<Val> {
F32Const(x) => Val::F32(x.bits),
F64Const(x) => Val::F64(x.bits),
V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())),
RefNull(RefType::Extern) => Val::ExternRef(None),
RefNull(RefType::Func) => Val::FuncRef(None),
RefNull(HeapType::Extern) => Val::ExternRef(None),
RefNull(HeapType::Func) => Val::FuncRef(None),
RefExtern(x) => Val::ExternRef(Some(ExternRef::new(*x))),
other => bail!("couldn't convert {:?} to a runtime value", other),
})
@@ -409,7 +409,7 @@ fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result<bool>
(Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b),
(Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b),
(Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b),
(Val::ExternRef(x), wast::AssertExpression::RefNull(wast::RefType::Extern)) => x.is_none(),
(Val::ExternRef(x), wast::AssertExpression::RefNull(HeapType::Extern)) => x.is_none(),
(Val::ExternRef(x), wast::AssertExpression::RefExtern(y)) => {
if let Some(x) = x {
let x = x
@@ -421,7 +421,7 @@ fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result<bool>
false
}
}
(Val::FuncRef(x), wast::AssertExpression::RefNull(wast::RefType::Func)) => x.is_none(),
(Val::FuncRef(x), wast::AssertExpression::RefNull(HeapType::Func)) => x.is_none(),
_ => bail!(
"don't know how to compare {:?} and {:?} yet",
actual,