mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 19:26:43 +01:00
add support for global vars
This commit is contained in:
@@ -11,11 +11,18 @@ use ValType::*;
|
||||
pub struct BaseModule {
|
||||
pub types: Vec<FunctionType>,
|
||||
pub function_imports: Vec<(&'static str, String, u32)>,
|
||||
pub global_imports: Vec<(&'static str, String, GlobalType)>,
|
||||
pub functions: Vec<u32>,
|
||||
pub exports: Vec<(&'static str, u32)>,
|
||||
pub memory: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GlobalType {
|
||||
pub type_: ValType,
|
||||
pub mutable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionType {
|
||||
pub params: Vec<ValType>,
|
||||
@@ -23,7 +30,7 @@ pub struct FunctionType {
|
||||
}
|
||||
|
||||
impl BaseModule {
|
||||
pub fn for_format_version(version: u32) -> Result<BaseModule> {
|
||||
pub fn for_format_version(version: u8) -> Result<BaseModule> {
|
||||
if version != 1 {
|
||||
bail!("Unsupported format version ({})", version);
|
||||
}
|
||||
@@ -68,24 +75,36 @@ impl BaseModule {
|
||||
);
|
||||
}
|
||||
|
||||
let mut global_imports = vec![];
|
||||
for i in 0..16 {
|
||||
global_imports.push((
|
||||
"env",
|
||||
format!("g_reserved{}", i),
|
||||
GlobalType {
|
||||
type_: I32,
|
||||
mutable: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let first_function = functions.len() as u32;
|
||||
|
||||
Ok(BaseModule {
|
||||
types,
|
||||
function_imports: functions,
|
||||
global_imports,
|
||||
functions: vec![lookup_type(&type_map, &[I32], None)],
|
||||
exports: vec![("tic", first_function)],
|
||||
memory: 4,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
fn inner(m: &BaseModule, path: &Path) -> Result<()> {
|
||||
pub fn to_wasm(&self) -> Vec<u8> {
|
||||
let mut module = Module::new();
|
||||
|
||||
{
|
||||
let mut types = TypeSection::new();
|
||||
for type_ in &m.types {
|
||||
for type_ in &self.types {
|
||||
types.function(type_.params.iter().cloned(), type_.result.iter().cloned());
|
||||
}
|
||||
module.section(&types);
|
||||
@@ -94,15 +113,26 @@ impl BaseModule {
|
||||
{
|
||||
let mut imports = ImportSection::new();
|
||||
|
||||
for (module, name, type_) in &m.function_imports {
|
||||
for (module, name, type_) in &self.function_imports {
|
||||
imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
|
||||
}
|
||||
|
||||
for (module, name, import) in &self.global_imports {
|
||||
imports.import(
|
||||
*module,
|
||||
Some(name.as_str()),
|
||||
EntityType::Global(wasm_encoder::GlobalType {
|
||||
val_type: import.type_,
|
||||
mutable: import.mutable,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
imports.import(
|
||||
"env",
|
||||
Some("memory"),
|
||||
MemoryType {
|
||||
minimum: m.memory as u64,
|
||||
minimum: self.memory as u64,
|
||||
maximum: None,
|
||||
memory64: false,
|
||||
},
|
||||
@@ -114,7 +144,7 @@ impl BaseModule {
|
||||
{
|
||||
let mut functions = FunctionSection::new();
|
||||
|
||||
for type_ in &m.functions {
|
||||
for type_ in &self.functions {
|
||||
functions.function(*type_);
|
||||
}
|
||||
|
||||
@@ -124,7 +154,7 @@ impl BaseModule {
|
||||
{
|
||||
let mut exports = ExportSection::new();
|
||||
|
||||
for (name, fnc) in &m.exports {
|
||||
for (name, fnc) in &self.exports {
|
||||
exports.export(*name, Export::Function(*fnc));
|
||||
}
|
||||
|
||||
@@ -134,7 +164,7 @@ impl BaseModule {
|
||||
{
|
||||
let mut code = CodeSection::new();
|
||||
|
||||
for _ in &m.functions {
|
||||
for _ in &self.functions {
|
||||
let mut function = Function::new([]);
|
||||
function.instruction(&Instruction::End);
|
||||
code.function(&function);
|
||||
@@ -143,13 +173,12 @@ impl BaseModule {
|
||||
module.section(&code);
|
||||
}
|
||||
|
||||
let data = module.finish();
|
||||
|
||||
File::create(path)?.write_all(&data)?;
|
||||
|
||||
Ok(())
|
||||
module.finish()
|
||||
}
|
||||
inner(self, path.as_ref())
|
||||
|
||||
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
File::create(path)?.write_all(&self.to_wasm())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,20 @@ fn main() -> Result<()> {
|
||||
if let Some(cmd) = args.subcommand()? {
|
||||
match cmd.as_str() {
|
||||
"make-base" => {
|
||||
let version: u32 = args.free_from_str()?;
|
||||
let version: u8 = args.free_from_str()?;
|
||||
BaseModule::for_format_version(version)?
|
||||
.write_to_file(format!("base{}.wasm", version))?;
|
||||
}
|
||||
"pack" => {
|
||||
let version: u32 = args.opt_value_from_str(["-v", "--version"])?.unwrap_or(1);
|
||||
let version: u8 = args.opt_value_from_str(["-v", "--version"])?.unwrap_or(1);
|
||||
let source: PathBuf = args.free_from_str()?;
|
||||
let dest: PathBuf = args.free_from_str()?;
|
||||
pack::pack(&source, &dest, version)?;
|
||||
pack::pack_file(&source, &dest, version)?;
|
||||
}
|
||||
"unpack" => {
|
||||
let source: PathBuf = args.free_from_str()?;
|
||||
let dest: PathBuf = args.free_from_str()?;
|
||||
pack::unpack_file(&source, &dest)?;
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Unknown subcommand '{}'", cmd);
|
||||
@@ -32,7 +37,6 @@ fn main() -> Result<()> {
|
||||
print_help();
|
||||
}
|
||||
|
||||
BaseModule::for_format_version(1)?.write_to_file("base.wasm")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::base_module::{self, BaseModule, FunctionType};
|
||||
use crate::base_module::{self, BaseModule, FunctionType, GlobalType};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use enc::ValType;
|
||||
use std::{
|
||||
@@ -9,11 +9,11 @@ use std::{
|
||||
};
|
||||
use wasm_encoder as enc;
|
||||
use wasmparser::{
|
||||
ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader, ImportSectionEntryType,
|
||||
ImportSectionReader, TypeSectionReader,
|
||||
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
|
||||
ImportSectionEntryType, ImportSectionReader, TypeSectionReader,
|
||||
};
|
||||
|
||||
pub fn pack(source: &Path, dest: &Path, version: u32) -> Result<()> {
|
||||
pub fn pack_file(source: &Path, dest: &Path, version: u8) -> Result<()> {
|
||||
let base = BaseModule::for_format_version(version)?;
|
||||
|
||||
let mut source_data = vec![];
|
||||
@@ -29,6 +29,60 @@ pub fn pack(source: &Path, dest: &Path, version: u32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack_file(source: &Path, dest: &Path) -> Result<()> {
|
||||
let mut source_data = vec![];
|
||||
File::open(source)?.read_to_end(&mut source_data)?;
|
||||
let unpacked = unpack(source_data)?;
|
||||
File::create(dest)?.write_all(&unpacked)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
|
||||
let version = data[0];
|
||||
if version == 0 {
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
let base_data = BaseModule::for_format_version(version)?.to_wasm();
|
||||
|
||||
let mut data = &data[1..];
|
||||
let mut base_data = base_data.as_slice();
|
||||
|
||||
let mut dest = base_data[..8].to_vec();
|
||||
base_data = &base_data[8..];
|
||||
|
||||
fn section_length(data: &[u8]) -> Result<usize> {
|
||||
let mut reader = BinaryReader::new_with_offset(&data[1..], 1);
|
||||
let inner_len = reader.read_var_u32()? as usize;
|
||||
let header_len = reader.original_position();
|
||||
let len = header_len + inner_len;
|
||||
dbg!(len);
|
||||
if len > data.len() {
|
||||
bail!("Section length greater than size of the rest of the file");
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn copy_section<'a>(dest: &mut Vec<u8>, source: &'a [u8]) -> Result<&'a [u8]> {
|
||||
let len = section_length(source)?;
|
||||
dest.extend_from_slice(&source[..len]);
|
||||
Ok(&source[len..])
|
||||
}
|
||||
|
||||
while !data.is_empty() || !base_data.is_empty() {
|
||||
if !data.is_empty() && (base_data.is_empty() || data[0] <= base_data[0]) {
|
||||
if !base_data.is_empty() && data[0] == base_data[0] {
|
||||
base_data = &base_data[section_length(base_data)?..];
|
||||
}
|
||||
data = copy_section(&mut dest, data)?;
|
||||
} else {
|
||||
base_data = copy_section(&mut dest, base_data)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dest)
|
||||
}
|
||||
|
||||
fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
|
||||
use wasmparser::Type::*;
|
||||
Ok(match *type_ {
|
||||
@@ -49,6 +103,7 @@ struct ParsedModule<'a> {
|
||||
data: &'a [u8],
|
||||
types: Section<Vec<base_module::FunctionType>>,
|
||||
imports: Section<ImportSection>,
|
||||
globals: Option<Section<u32>>,
|
||||
functions: Section<Vec<u32>>,
|
||||
exports: Section<Vec<(String, u32)>>,
|
||||
function_bodies: Vec<wasmparser::FunctionBody<'a>>,
|
||||
@@ -60,6 +115,7 @@ impl<'a> ParsedModule<'a> {
|
||||
|
||||
let mut type_section = None;
|
||||
let mut import_section = None;
|
||||
let mut global_section = None;
|
||||
let mut function_section = None;
|
||||
let mut export_section = None;
|
||||
let mut function_bodies = Vec::new();
|
||||
@@ -87,6 +143,9 @@ impl<'a> ParsedModule<'a> {
|
||||
Payload::ImportSection(reader) => {
|
||||
import_section = Some(Section::new(range, ImportSection::parse(reader)?));
|
||||
}
|
||||
Payload::GlobalSection(reader) => {
|
||||
global_section = Some(Section::new(range, reader.get_count()));
|
||||
}
|
||||
Payload::FunctionSection(reader) => {
|
||||
function_section = Some(Section::new(range, read_function_section(reader)?));
|
||||
}
|
||||
@@ -107,6 +166,7 @@ impl<'a> ParsedModule<'a> {
|
||||
data,
|
||||
types: type_section.ok_or_else(|| anyhow!("No type section found"))?,
|
||||
imports: import_section.ok_or_else(|| anyhow!("No import section found"))?,
|
||||
globals: global_section,
|
||||
functions: function_section.ok_or_else(|| anyhow!("No function section found"))?,
|
||||
exports: export_section.ok_or_else(|| anyhow!("No export section found"))?,
|
||||
function_bodies,
|
||||
@@ -146,6 +206,8 @@ impl<'a> ParsedModule<'a> {
|
||||
|
||||
let mut function_map = HashMap::new();
|
||||
let mut function_count = 0;
|
||||
let mut global_map = HashMap::new();
|
||||
let mut global_count = 0;
|
||||
|
||||
if uses_base_types {
|
||||
if self.imports.data.memory > base.memory {
|
||||
@@ -185,16 +247,54 @@ impl<'a> ParsedModule<'a> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function_count += base.function_imports.len();
|
||||
|
||||
let base_global_import_map: HashMap<(String, String), (GlobalType, u32)> = base
|
||||
.global_imports
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, (module, field, type_))| {
|
||||
(
|
||||
(module.to_string(), field.clone()),
|
||||
(type_.clone(), idx as u32),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
for (idx, glb) in self.imports.data.globals.iter().enumerate() {
|
||||
if let Some((base_type, base_idx)) =
|
||||
base_global_import_map.get(&(glb.module.clone(), glb.field.clone()))
|
||||
{
|
||||
if base_type == &glb.type_ {
|
||||
global_map.insert(idx as u32, *base_idx);
|
||||
} else {
|
||||
bail!(
|
||||
"Import global {}.{} has incompatible type",
|
||||
glb.module,
|
||||
glb.field
|
||||
);
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"Import global {}.{} not found in base",
|
||||
glb.module,
|
||||
glb.field
|
||||
);
|
||||
}
|
||||
}
|
||||
global_count += base.global_imports.len();
|
||||
} else {
|
||||
copy_section(&mut module, &self.data[self.imports.range.clone()])?;
|
||||
|
||||
function_map = (0..self.imports.data.functions.len() as u32)
|
||||
.map(|i| (i, i))
|
||||
.collect();
|
||||
|
||||
copy_section(&mut module, &self.data[self.imports.range.clone()])?;
|
||||
|
||||
function_count += self.imports.data.functions.len();
|
||||
|
||||
global_map = (0..self.imports.data.globals.len() as u32)
|
||||
.map(|i| (i, i))
|
||||
.collect();
|
||||
global_count += self.imports.data.globals.len();
|
||||
}
|
||||
|
||||
let functions = {
|
||||
@@ -246,6 +346,17 @@ impl<'a> ParsedModule<'a> {
|
||||
module.section(&function_section);
|
||||
}
|
||||
|
||||
if let Some(ref globals) = self.globals {
|
||||
copy_section(&mut module, &self.data[globals.range.clone()])?;
|
||||
for i in 0..globals.data {
|
||||
global_map.insert(
|
||||
self.imports.data.globals.len() as u32 + i,
|
||||
global_count as u32,
|
||||
);
|
||||
global_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut base_exports = base.exports.clone();
|
||||
base_exports.sort();
|
||||
@@ -274,7 +385,12 @@ impl<'a> ParsedModule<'a> {
|
||||
let mut code_section = enc::CodeSection::new();
|
||||
|
||||
for (_, function) in &functions {
|
||||
code_section.function(&remap_function(function, &type_map, &function_map)?);
|
||||
code_section.function(&remap_function(
|
||||
function,
|
||||
&type_map,
|
||||
&function_map,
|
||||
&global_map,
|
||||
)?);
|
||||
}
|
||||
|
||||
module.section(&code_section);
|
||||
@@ -292,10 +408,7 @@ fn copy_section(module: &mut wasm_encoder::Module, data: &[u8]) -> Result<()> {
|
||||
let data = &data[reader.current_position()..];
|
||||
assert!(data.len() == size as usize);
|
||||
|
||||
module.section(&wasm_encoder::RawSection {
|
||||
id,
|
||||
data: &data[reader.current_position()..],
|
||||
});
|
||||
module.section(&wasm_encoder::RawSection { id, data });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -336,12 +449,14 @@ impl<T> Section<T> {
|
||||
struct ImportSection {
|
||||
memory: u32,
|
||||
functions: Vec<FunctionImport>,
|
||||
globals: Vec<GlobalImport>,
|
||||
}
|
||||
|
||||
impl ImportSection {
|
||||
fn parse(reader: ImportSectionReader) -> Result<ImportSection> {
|
||||
let mut memory = 0;
|
||||
let mut functions = vec![];
|
||||
let mut globals = vec![];
|
||||
|
||||
for import in reader {
|
||||
let import = import?;
|
||||
@@ -367,6 +482,16 @@ impl ImportSection {
|
||||
}
|
||||
memory = mem.maximum.unwrap_or(mem.initial) as u32;
|
||||
}
|
||||
ImportSectionEntryType::Global(glbl) => {
|
||||
globals.push(GlobalImport {
|
||||
module: import.module.to_string(),
|
||||
field: field.to_string(),
|
||||
type_: GlobalType {
|
||||
type_: to_val_type(&glbl.content_type)?,
|
||||
mutable: glbl.mutable,
|
||||
},
|
||||
});
|
||||
}
|
||||
_ => bail!("Unsupported import item {:?}", import.ty),
|
||||
}
|
||||
} else {
|
||||
@@ -381,7 +506,11 @@ impl ImportSection {
|
||||
bail!("No memory import found");
|
||||
}
|
||||
|
||||
Ok(ImportSection { memory, functions })
|
||||
Ok(ImportSection {
|
||||
memory,
|
||||
functions,
|
||||
globals,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +521,13 @@ struct FunctionImport {
|
||||
type_: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GlobalImport {
|
||||
module: String,
|
||||
field: String,
|
||||
type_: GlobalType,
|
||||
}
|
||||
|
||||
fn read_function_section(reader: FunctionSectionReader) -> Result<Vec<u32>> {
|
||||
let mut functions = vec![];
|
||||
for func_type in reader {
|
||||
@@ -418,6 +554,7 @@ fn remap_function(
|
||||
reader: &FunctionBody,
|
||||
type_map: &HashMap<u32, u32>,
|
||||
function_map: &HashMap<u32, u32>,
|
||||
global_map: &HashMap<u32, u32>,
|
||||
) -> Result<enc::Function> {
|
||||
let mut locals = Vec::new();
|
||||
for local in reader.get_locals_reader()? {
|
||||
@@ -440,6 +577,12 @@ fn remap_function(
|
||||
})
|
||||
};
|
||||
|
||||
let global_idx = |idx: u32| -> Result<u32> {
|
||||
Ok(*global_map
|
||||
.get(&idx)
|
||||
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
|
||||
};
|
||||
|
||||
fn mem(m: wasmparser::MemoryImmediate) -> enc::MemArg {
|
||||
enc::MemArg {
|
||||
offset: m.offset,
|
||||
@@ -481,8 +624,8 @@ fn remap_function(
|
||||
De::LocalGet { local_index } => En::LocalGet(local_index),
|
||||
De::LocalSet { local_index } => En::LocalSet(local_index),
|
||||
De::LocalTee { local_index } => En::LocalTee(local_index),
|
||||
De::GlobalGet { global_index } => En::GlobalGet(global_index),
|
||||
De::GlobalSet { global_index } => En::GlobalSet(global_index),
|
||||
De::GlobalGet { global_index } => En::GlobalGet(global_idx(global_index)?),
|
||||
De::GlobalSet { global_index } => En::GlobalSet(global_idx(global_index)?),
|
||||
De::I32Load { memarg } => En::I32Load(mem(memarg)),
|
||||
De::I64Load { memarg } => En::I64Load(mem(memarg)),
|
||||
De::F32Load { memarg } => En::F32Load(mem(memarg)),
|
||||
|
||||
@@ -92,6 +92,10 @@ async function runModule(data) {
|
||||
importObject.env['reserved' + i] = () => { };
|
||||
}
|
||||
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
importObject.env['g_reserved' + i] = 0;
|
||||
}
|
||||
|
||||
let instance = new WebAssembly.Instance(await WebAssembly.compile(data), importObject);
|
||||
|
||||
let buffer = imageData.data;
|
||||
|
||||
Reference in New Issue
Block a user