Update rustfmt to 0.9.0.
This commit is contained in:
@@ -135,7 +135,7 @@ struct TranslationState {
|
||||
|
||||
/// Holds mappings between the function and signatures indexes in the Wasm module and their
|
||||
/// references as imports of the Cretonne IL function.
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionImports {
|
||||
/// Mappings index in function index space -> index in function local imports
|
||||
pub functions: HashMap<FunctionIndex, FuncRef>,
|
||||
@@ -153,16 +153,17 @@ impl FunctionImports {
|
||||
}
|
||||
|
||||
/// Returns a well-formed Cretonne IL function from a wasm function body and a signature.
|
||||
pub fn translate_function_body(parser: &mut Parser,
|
||||
function_index: FunctionIndex,
|
||||
sig: Signature,
|
||||
locals: &[(usize, Type)],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
signatures: &[Signature],
|
||||
functions: &[SignatureIndex],
|
||||
il_builder: &mut ILBuilder<Local>,
|
||||
runtime: &mut WasmRuntime)
|
||||
-> Result<(Function, FunctionImports), String> {
|
||||
pub fn translate_function_body(
|
||||
parser: &mut Parser,
|
||||
function_index: FunctionIndex,
|
||||
sig: Signature,
|
||||
locals: &[(usize, Type)],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
signatures: &[Signature],
|
||||
functions: &[SignatureIndex],
|
||||
il_builder: &mut ILBuilder<Local>,
|
||||
runtime: &mut WasmRuntime,
|
||||
) -> Result<(Function, FunctionImports), String> {
|
||||
runtime.next_function();
|
||||
// First we build the Function object with its name and signature
|
||||
let mut func = Function::new();
|
||||
@@ -216,39 +217,45 @@ pub fn translate_function_body(parser: &mut Parser,
|
||||
// We initialize the control stack with the implicit function block
|
||||
let end_ebb = builder.create_ebb();
|
||||
control_stack.push(ControlStackFrame::Block {
|
||||
destination: end_ebb,
|
||||
original_stack_size: 0,
|
||||
return_values: sig.return_types
|
||||
.iter()
|
||||
.map(|argty| argty.value_type)
|
||||
.collect(),
|
||||
reachable: false,
|
||||
});
|
||||
destination: end_ebb,
|
||||
original_stack_size: 0,
|
||||
return_values: sig.return_types
|
||||
.iter()
|
||||
.map(|argty| argty.value_type)
|
||||
.collect(),
|
||||
reachable: false,
|
||||
});
|
||||
// Now the main loop that reads every wasm instruction and translates it
|
||||
loop {
|
||||
let parser_state = parser.read();
|
||||
match *parser_state {
|
||||
ParserState::CodeOperator(ref op) => {
|
||||
debug_assert!(state.phantom_unreachable_stack_depth == 0 ||
|
||||
state.real_unreachable_stack_depth > 0);
|
||||
debug_assert!(
|
||||
state.phantom_unreachable_stack_depth == 0 ||
|
||||
state.real_unreachable_stack_depth > 0
|
||||
);
|
||||
if state.real_unreachable_stack_depth > 0 {
|
||||
translate_unreachable_operator(op,
|
||||
&mut builder,
|
||||
&mut stack,
|
||||
&mut control_stack,
|
||||
&mut state)
|
||||
translate_unreachable_operator(
|
||||
op,
|
||||
&mut builder,
|
||||
&mut stack,
|
||||
&mut control_stack,
|
||||
&mut state,
|
||||
)
|
||||
} else {
|
||||
translate_operator(op,
|
||||
&mut builder,
|
||||
runtime,
|
||||
&mut stack,
|
||||
&mut control_stack,
|
||||
&mut state,
|
||||
&sig,
|
||||
&functions,
|
||||
&signatures,
|
||||
&exports,
|
||||
&mut func_imports)
|
||||
translate_operator(
|
||||
op,
|
||||
&mut builder,
|
||||
runtime,
|
||||
&mut stack,
|
||||
&mut control_stack,
|
||||
&mut state,
|
||||
&sig,
|
||||
&functions,
|
||||
&signatures,
|
||||
&exports,
|
||||
&mut func_imports,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,17 +288,19 @@ pub fn translate_function_body(parser: &mut Parser,
|
||||
|
||||
/// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted
|
||||
/// a return.
|
||||
fn translate_operator(op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
runtime: &mut WasmRuntime,
|
||||
stack: &mut Vec<Value>,
|
||||
control_stack: &mut Vec<ControlStackFrame>,
|
||||
state: &mut TranslationState,
|
||||
sig: &Signature,
|
||||
functions: &[SignatureIndex],
|
||||
signatures: &[Signature],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
func_imports: &mut FunctionImports) {
|
||||
fn translate_operator(
|
||||
op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
runtime: &mut WasmRuntime,
|
||||
stack: &mut Vec<Value>,
|
||||
control_stack: &mut Vec<ControlStackFrame>,
|
||||
state: &mut TranslationState,
|
||||
sig: &Signature,
|
||||
functions: &[SignatureIndex],
|
||||
signatures: &[Signature],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
func_imports: &mut FunctionImports,
|
||||
) {
|
||||
// This big match treats all Wasm code operators.
|
||||
match *op {
|
||||
/********************************** Locals ****************************************
|
||||
@@ -357,11 +366,11 @@ fn translate_operator(op: &Operator,
|
||||
Err(_) => {}
|
||||
}
|
||||
control_stack.push(ControlStackFrame::Block {
|
||||
destination: next,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
destination: next,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
}
|
||||
Operator::Loop { ty } => {
|
||||
let loop_body = builder.create_ebb();
|
||||
@@ -374,12 +383,12 @@ fn translate_operator(op: &Operator,
|
||||
}
|
||||
builder.ins().jump(loop_body, &[]);
|
||||
control_stack.push(ControlStackFrame::Loop {
|
||||
destination: next,
|
||||
header: loop_body,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
destination: next,
|
||||
header: loop_body,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
builder.switch_to_block(loop_body, &[]);
|
||||
}
|
||||
Operator::If { ty } => {
|
||||
@@ -399,12 +408,12 @@ fn translate_operator(op: &Operator,
|
||||
Err(_) => {}
|
||||
}
|
||||
control_stack.push(ControlStackFrame::If {
|
||||
destination: if_not,
|
||||
branch_inst: jump_inst,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
destination: if_not,
|
||||
branch_inst: jump_inst,
|
||||
return_values: translate_type(ty).unwrap(),
|
||||
original_stack_size: stack.len(),
|
||||
reachable: false,
|
||||
});
|
||||
}
|
||||
Operator::Else => {
|
||||
// We take the control frame pushed by the if, use its ebb as the else body
|
||||
@@ -434,9 +443,10 @@ fn translate_operator(op: &Operator,
|
||||
if !builder.is_unreachable() || !builder.is_pristine() {
|
||||
let cut_index = stack.len() - frame.return_values().len();
|
||||
let jump_args = stack.split_off(cut_index);
|
||||
builder
|
||||
.ins()
|
||||
.jump(frame.following_code(), jump_args.as_slice());
|
||||
builder.ins().jump(
|
||||
frame.following_code(),
|
||||
jump_args.as_slice(),
|
||||
);
|
||||
}
|
||||
builder.switch_to_block(frame.following_code(), frame.return_values());
|
||||
builder.seal_block(frame.following_code());
|
||||
@@ -478,9 +488,10 @@ fn translate_operator(op: &Operator,
|
||||
let cut_index = stack.len() - frame.return_values().len();
|
||||
stack.split_off(cut_index)
|
||||
};
|
||||
builder
|
||||
.ins()
|
||||
.jump(frame.br_destination(), jump_args.as_slice());
|
||||
builder.ins().jump(
|
||||
frame.br_destination(),
|
||||
jump_args.as_slice(),
|
||||
);
|
||||
// We signal that all the code that follows until the next End is unreachable
|
||||
frame.set_reachable();
|
||||
state.real_unreachable_stack_depth = 1 + relative_depth as usize;
|
||||
@@ -495,9 +506,11 @@ fn translate_operator(op: &Operator,
|
||||
let cut_index = stack.len() - frame.return_values().len();
|
||||
stack.split_off(cut_index)
|
||||
};
|
||||
builder
|
||||
.ins()
|
||||
.brnz(val, frame.br_destination(), jump_args.as_slice());
|
||||
builder.ins().brnz(
|
||||
val,
|
||||
frame.br_destination(),
|
||||
jump_args.as_slice(),
|
||||
);
|
||||
// The values returned by the branch are still available for the reachable
|
||||
// code that comes after it
|
||||
frame.set_reachable();
|
||||
@@ -545,10 +558,9 @@ fn translate_operator(op: &Operator,
|
||||
let cut_index = stack.len() - jump_args_count;
|
||||
let jump_args = stack.split_off(cut_index);
|
||||
let jt = builder.create_jump_table();
|
||||
let dest_ebbs: HashMap<usize, Ebb> = depths
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(HashMap::new(), |mut acc, (index, &depth)| {
|
||||
let dest_ebbs: HashMap<usize, Ebb> =
|
||||
depths.iter().enumerate().fold(HashMap::new(), |mut acc,
|
||||
(index, &depth)| {
|
||||
if acc.get(&(depth as usize)).is_none() {
|
||||
let branch_ebb = builder.create_ebb();
|
||||
builder.insert_jump_table_entry(jt, index, branch_ebb);
|
||||
@@ -592,15 +604,18 @@ fn translate_operator(op: &Operator,
|
||||
let args_num = args_count(function_index as usize, functions, signatures);
|
||||
let cut_index = stack.len() - args_num;
|
||||
let call_args = stack.split_off(cut_index);
|
||||
let internal_function_index = find_function_import(function_index as usize,
|
||||
builder,
|
||||
func_imports,
|
||||
functions,
|
||||
exports,
|
||||
signatures);
|
||||
let call_inst = builder
|
||||
.ins()
|
||||
.call(internal_function_index, call_args.as_slice());
|
||||
let internal_function_index = find_function_import(
|
||||
function_index as usize,
|
||||
builder,
|
||||
func_imports,
|
||||
functions,
|
||||
exports,
|
||||
signatures,
|
||||
);
|
||||
let call_inst = builder.ins().call(
|
||||
internal_function_index,
|
||||
call_args.as_slice(),
|
||||
);
|
||||
let ret_values = builder.inst_results(call_inst);
|
||||
for val in ret_values {
|
||||
stack.push(*val);
|
||||
@@ -1107,9 +1122,11 @@ fn translate_operator(op: &Operator,
|
||||
Operator::I32LeU | Operator::I64LeU => {
|
||||
let arg2 = stack.pop().unwrap();
|
||||
let arg1 = stack.pop().unwrap();
|
||||
let val = builder
|
||||
.ins()
|
||||
.icmp(IntCC::UnsignedLessThanOrEqual, arg1, arg2);
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::UnsignedLessThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
stack.push(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32GtS | Operator::I64GtS => {
|
||||
@@ -1127,17 +1144,21 @@ fn translate_operator(op: &Operator,
|
||||
Operator::I32GeS | Operator::I64GeS => {
|
||||
let arg2 = stack.pop().unwrap();
|
||||
let arg1 = stack.pop().unwrap();
|
||||
let val = builder
|
||||
.ins()
|
||||
.icmp(IntCC::SignedGreaterThanOrEqual, arg1, arg2);
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::SignedGreaterThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
stack.push(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32GeU | Operator::I64GeU => {
|
||||
let arg2 = stack.pop().unwrap();
|
||||
let arg1 = stack.pop().unwrap();
|
||||
let val = builder
|
||||
.ins()
|
||||
.icmp(IntCC::UnsignedGreaterThanOrEqual, arg1, arg2);
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::UnsignedGreaterThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
stack.push(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32Eqz | Operator::I64Eqz => {
|
||||
@@ -1199,11 +1220,13 @@ fn translate_operator(op: &Operator,
|
||||
/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
|
||||
/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
|
||||
/// portion so the translation state muts be updated accordingly.
|
||||
fn translate_unreachable_operator(op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
stack: &mut Vec<Value>,
|
||||
control_stack: &mut Vec<ControlStackFrame>,
|
||||
state: &mut TranslationState) {
|
||||
fn translate_unreachable_operator(
|
||||
op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
stack: &mut Vec<Value>,
|
||||
control_stack: &mut Vec<ControlStackFrame>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
// We don't translate because the code is unreachable
|
||||
// Nevertheless we have to record a phantom stack for this code
|
||||
// to know when the unreachable code ends
|
||||
@@ -1253,15 +1276,15 @@ fn translate_unreachable_operator(op: &Operator,
|
||||
} else {
|
||||
// Encountering an real else means that the code in the else
|
||||
// clause is reachable again
|
||||
let (branch_inst, original_stack_size) = match &control_stack[control_stack.len() -
|
||||
1] {
|
||||
&ControlStackFrame::If {
|
||||
branch_inst,
|
||||
original_stack_size,
|
||||
..
|
||||
} => (branch_inst, original_stack_size),
|
||||
_ => panic!("should not happen"),
|
||||
};
|
||||
let (branch_inst, original_stack_size) =
|
||||
match &control_stack[control_stack.len() - 1] {
|
||||
&ControlStackFrame::If {
|
||||
branch_inst,
|
||||
original_stack_size,
|
||||
..
|
||||
} => (branch_inst, original_stack_size),
|
||||
_ => panic!("should not happen"),
|
||||
};
|
||||
// We change the target of the branch instruction
|
||||
let else_ebb = builder.create_ebb();
|
||||
builder.change_jump_destination(branch_inst, else_ebb);
|
||||
@@ -1279,22 +1302,24 @@ fn translate_unreachable_operator(op: &Operator,
|
||||
}
|
||||
}
|
||||
|
||||
fn args_count(index: FunctionIndex,
|
||||
functions: &[SignatureIndex],
|
||||
signatures: &[Signature])
|
||||
-> usize {
|
||||
fn args_count(
|
||||
index: FunctionIndex,
|
||||
functions: &[SignatureIndex],
|
||||
signatures: &[Signature],
|
||||
) -> usize {
|
||||
signatures[functions[index] as usize].argument_types.len()
|
||||
}
|
||||
|
||||
// Given a index in the function index space, search for it in the function imports and if it is
|
||||
// not there add it to the function imports.
|
||||
fn find_function_import(index: FunctionIndex,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
func_imports: &mut FunctionImports,
|
||||
functions: &[SignatureIndex],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
signatures: &[Signature])
|
||||
-> FuncRef {
|
||||
fn find_function_import(
|
||||
index: FunctionIndex,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
func_imports: &mut FunctionImports,
|
||||
functions: &[SignatureIndex],
|
||||
exports: &Option<HashMap<FunctionIndex, String>>,
|
||||
signatures: &[Signature],
|
||||
) -> FuncRef {
|
||||
match func_imports.functions.get(&index) {
|
||||
Some(local_index) => return *local_index,
|
||||
None => {}
|
||||
@@ -1303,21 +1328,18 @@ fn find_function_import(index: FunctionIndex,
|
||||
let sig_index = functions[index];
|
||||
match func_imports.signatures.get(&(sig_index as usize)) {
|
||||
Some(local_sig_index) => {
|
||||
let local_func_index =
|
||||
builder.import_function(ExtFuncData {
|
||||
name: match exports {
|
||||
&None => FunctionName::new(""),
|
||||
&Some(ref exports) => {
|
||||
match exports.get(&index) {
|
||||
None => FunctionName::new(""),
|
||||
Some(name) => {
|
||||
FunctionName::new(name.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
signature: *local_sig_index,
|
||||
});
|
||||
let local_func_index = builder.import_function(ExtFuncData {
|
||||
name: match exports {
|
||||
&None => FunctionName::new(""),
|
||||
&Some(ref exports) => {
|
||||
match exports.get(&index) {
|
||||
None => FunctionName::new(""),
|
||||
Some(name) => FunctionName::new(name.clone()),
|
||||
}
|
||||
}
|
||||
},
|
||||
signature: *local_sig_index,
|
||||
});
|
||||
func_imports.functions.insert(index, local_func_index);
|
||||
return local_func_index;
|
||||
}
|
||||
@@ -1325,38 +1347,40 @@ fn find_function_import(index: FunctionIndex,
|
||||
};
|
||||
// We have to import the signature
|
||||
let sig_local_index = builder.import_signature(signatures[sig_index as usize].clone());
|
||||
func_imports
|
||||
.signatures
|
||||
.insert(sig_index as usize, sig_local_index);
|
||||
let local_func_index =
|
||||
builder.import_function(ExtFuncData {
|
||||
name: match exports {
|
||||
&None => FunctionName::new(""),
|
||||
&Some(ref exports) => {
|
||||
match exports.get(&index) {
|
||||
None => FunctionName::new(""),
|
||||
Some(name) => FunctionName::new(name.clone()),
|
||||
}
|
||||
}
|
||||
},
|
||||
signature: sig_local_index,
|
||||
});
|
||||
func_imports.signatures.insert(
|
||||
sig_index as usize,
|
||||
sig_local_index,
|
||||
);
|
||||
let local_func_index = builder.import_function(ExtFuncData {
|
||||
name: match exports {
|
||||
&None => FunctionName::new(""),
|
||||
&Some(ref exports) => {
|
||||
match exports.get(&index) {
|
||||
None => FunctionName::new(""),
|
||||
Some(name) => FunctionName::new(name.clone()),
|
||||
}
|
||||
}
|
||||
},
|
||||
signature: sig_local_index,
|
||||
});
|
||||
func_imports.functions.insert(index, local_func_index);
|
||||
local_func_index
|
||||
}
|
||||
|
||||
fn find_signature_import(sig_index: SignatureIndex,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
func_imports: &mut FunctionImports,
|
||||
signatures: &[Signature])
|
||||
-> SigRef {
|
||||
fn find_signature_import(
|
||||
sig_index: SignatureIndex,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
func_imports: &mut FunctionImports,
|
||||
signatures: &[Signature],
|
||||
) -> SigRef {
|
||||
match func_imports.signatures.get(&(sig_index as usize)) {
|
||||
Some(local_sig_index) => return *local_sig_index,
|
||||
None => {}
|
||||
}
|
||||
let sig_local_index = builder.import_signature(signatures[sig_index as usize].clone());
|
||||
func_imports
|
||||
.signatures
|
||||
.insert(sig_index as usize, sig_local_index);
|
||||
func_imports.signatures.insert(
|
||||
sig_index as usize,
|
||||
sig_local_index,
|
||||
);
|
||||
sig_local_index
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ pub enum FunctionTranslation {
|
||||
Import(),
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
/// Mappings describing the relations between imports of the Cretonne IL functions and the
|
||||
/// functions in the WebAssembly module.
|
||||
pub struct ImportMappings {
|
||||
@@ -58,9 +58,10 @@ impl ImportMappings {
|
||||
/// [`Function`](../cretonne/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: &[u8],
|
||||
runtime: &mut WasmRuntime)
|
||||
-> Result<TranslationResult, String> {
|
||||
pub fn translate_module(
|
||||
data: &[u8],
|
||||
runtime: &mut WasmRuntime,
|
||||
) -> Result<TranslationResult, String> {
|
||||
let mut parser = Parser::new(data);
|
||||
match *parser.read() {
|
||||
ParserState::BeginWasm { .. } => {}
|
||||
@@ -207,9 +208,9 @@ pub fn translate_module(data: &[u8],
|
||||
}
|
||||
ParserState::EndWasm => {
|
||||
return Ok(TranslationResult {
|
||||
functions: Vec::new(),
|
||||
start_index: None,
|
||||
})
|
||||
functions: Vec::new(),
|
||||
start_index: None,
|
||||
})
|
||||
}
|
||||
ParserState::BeginSection { code: SectionCode::Data, .. } => {
|
||||
match parse_data_section(&mut parser, runtime, &globals) {
|
||||
@@ -242,32 +243,36 @@ pub fn translate_module(data: &[u8],
|
||||
locals
|
||||
.iter()
|
||||
.map(|&(index, ref ty)| {
|
||||
(index as usize,
|
||||
match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("unsupported type for local variable"),
|
||||
})
|
||||
})
|
||||
(
|
||||
index as usize,
|
||||
match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("unsupported type for local variable"),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
ParserState::EndSection => break,
|
||||
_ => return Err(String::from(format!("wrong content in code section"))),
|
||||
};
|
||||
let signature = signatures[functions[function_index as usize] as usize].clone();
|
||||
match translate_function_body(&mut parser,
|
||||
function_index,
|
||||
signature,
|
||||
&locals,
|
||||
&exports,
|
||||
&signatures,
|
||||
&functions,
|
||||
&mut il_builder,
|
||||
runtime) {
|
||||
match translate_function_body(
|
||||
&mut parser,
|
||||
function_index,
|
||||
signature,
|
||||
&locals,
|
||||
&exports,
|
||||
&signatures,
|
||||
&functions,
|
||||
&mut il_builder,
|
||||
runtime,
|
||||
) {
|
||||
Ok((il_func, imports)) => {
|
||||
il_functions.push(FunctionTranslation::Code {
|
||||
il: il_func,
|
||||
imports: invert_hashmaps(imports),
|
||||
})
|
||||
il: il_func,
|
||||
imports: invert_hashmaps(imports),
|
||||
})
|
||||
}
|
||||
Err(s) => return Err(s),
|
||||
}
|
||||
@@ -285,9 +290,9 @@ pub fn translate_module(data: &[u8],
|
||||
}
|
||||
ParserState::EndWasm => {
|
||||
return Ok(TranslationResult {
|
||||
functions: il_functions,
|
||||
start_index,
|
||||
})
|
||||
functions: il_functions,
|
||||
start_index,
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -21,19 +21,20 @@ impl DummyRuntime {
|
||||
}
|
||||
|
||||
impl WasmRuntime for DummyRuntime {
|
||||
fn translate_get_global(&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex)
|
||||
-> Value {
|
||||
fn translate_get_global(
|
||||
&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex,
|
||||
) -> Value {
|
||||
let ref glob = self.globals.get(global_index as usize).unwrap();
|
||||
match glob.ty {
|
||||
I32 => builder.ins().iconst(glob.ty, -1),
|
||||
I64 => builder.ins().iconst(glob.ty, -1),
|
||||
F32 => builder.ins().f32const(Ieee32::with_bits(0xbf800000)), // -1.0
|
||||
F64 => {
|
||||
builder
|
||||
.ins()
|
||||
.f64const(Ieee64::with_bits(0xbff0000000000000))
|
||||
builder.ins().f64const(
|
||||
Ieee64::with_bits(0xbff0000000000000),
|
||||
)
|
||||
} // -1.0
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
@@ -48,19 +49,21 @@ impl WasmRuntime for DummyRuntime {
|
||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value {
|
||||
builder.ins().iconst(I32, -1)
|
||||
}
|
||||
fn translate_call_indirect<'a>(&self,
|
||||
builder: &'a mut FunctionBuilder<Local>,
|
||||
sig_ref: SigRef,
|
||||
index_val: Value,
|
||||
call_args: &[Value])
|
||||
-> &'a [Value] {
|
||||
fn translate_call_indirect<'a>(
|
||||
&self,
|
||||
builder: &'a mut FunctionBuilder<Local>,
|
||||
sig_ref: SigRef,
|
||||
index_val: Value,
|
||||
call_args: &[Value],
|
||||
) -> &'a [Value] {
|
||||
let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args);
|
||||
builder.inst_results(call_inst)
|
||||
}
|
||||
fn translate_memory_base_address(&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
_: MemoryIndex)
|
||||
-> Value {
|
||||
fn translate_memory_base_address(
|
||||
&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
_: MemoryIndex,
|
||||
) -> Value {
|
||||
builder.ins().iconst(I64, 0)
|
||||
}
|
||||
fn declare_global(&mut self, global: Global) {
|
||||
@@ -75,11 +78,12 @@ impl WasmRuntime for DummyRuntime {
|
||||
fn declare_memory(&mut self, _: Memory) {
|
||||
//We do nothing
|
||||
}
|
||||
fn declare_data_initialization(&mut self,
|
||||
_: MemoryIndex,
|
||||
_: usize,
|
||||
_: &[u8])
|
||||
-> Result<(), String> {
|
||||
fn declare_data_initialization(
|
||||
&mut self,
|
||||
_: MemoryIndex,
|
||||
_: usize,
|
||||
_: &[u8],
|
||||
) -> Result<(), String> {
|
||||
// We do nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,48 +14,56 @@ pub trait WasmRuntime {
|
||||
/// Declares a table to the runtime.
|
||||
fn declare_table(&mut self, table: Table);
|
||||
/// Fills a declared table with references to functions in the module.
|
||||
fn declare_table_elements(&mut self,
|
||||
table_index: TableIndex,
|
||||
offset: usize,
|
||||
elements: &[FunctionIndex]);
|
||||
fn declare_table_elements(
|
||||
&mut self,
|
||||
table_index: TableIndex,
|
||||
offset: usize,
|
||||
elements: &[FunctionIndex],
|
||||
);
|
||||
/// Declares a memory to the runtime
|
||||
fn declare_memory(&mut self, memory: Memory);
|
||||
/// Fills a declared memory with bytes at module instantiation.
|
||||
fn declare_data_initialization(&mut self,
|
||||
memory_index: MemoryIndex,
|
||||
offset: usize,
|
||||
data: &[u8])
|
||||
-> Result<(), String>;
|
||||
fn declare_data_initialization(
|
||||
&mut self,
|
||||
memory_index: MemoryIndex,
|
||||
offset: usize,
|
||||
data: &[u8],
|
||||
) -> Result<(), String>;
|
||||
/// Call this function after having declared all the runtime elements but prior to the
|
||||
/// function body translation.
|
||||
fn begin_translation(&mut self);
|
||||
/// Call this function between each function body translation.
|
||||
fn next_function(&mut self);
|
||||
/// Translates a `get_global` wasm instruction.
|
||||
fn translate_get_global(&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex)
|
||||
-> Value;
|
||||
fn translate_get_global(
|
||||
&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex,
|
||||
) -> Value;
|
||||
/// Translates a `set_global` wasm instruction.
|
||||
fn translate_set_global(&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex,
|
||||
val: Value);
|
||||
fn translate_set_global(
|
||||
&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
global_index: GlobalIndex,
|
||||
val: Value,
|
||||
);
|
||||
/// Translates a `grow_memory` wasm instruction. Returns the old size (in pages) of the memory.
|
||||
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, val: Value) -> Value;
|
||||
/// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory.
|
||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value;
|
||||
/// Returns the base address of a wasm memory as a Cretonne `Value`.
|
||||
fn translate_memory_base_address(&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
index: MemoryIndex)
|
||||
-> Value;
|
||||
fn translate_memory_base_address(
|
||||
&self,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
index: MemoryIndex,
|
||||
) -> Value;
|
||||
/// Translates a `call_indirect` wasm instruction. It involves looking up the value contained
|
||||
/// it the table at location `index_val` and calling the corresponding function.
|
||||
fn translate_call_indirect<'a>(&self,
|
||||
builder: &'a mut FunctionBuilder<Local>,
|
||||
sig_ref: SigRef,
|
||||
index_val: Value,
|
||||
call_args: &[Value])
|
||||
-> &'a [Value];
|
||||
fn translate_call_indirect<'a>(
|
||||
&self,
|
||||
builder: &'a mut FunctionBuilder<Local>,
|
||||
sig_ref: SigRef,
|
||||
index_val: Value,
|
||||
call_args: &[Value],
|
||||
) -> &'a [Value];
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ pub enum SectionParsingError {
|
||||
}
|
||||
|
||||
/// Reads the Type Section of the wasm module and returns the corresponding function signatures.
|
||||
pub fn parse_function_signatures(parser: &mut Parser)
|
||||
-> Result<Vec<Signature>, SectionParsingError> {
|
||||
pub fn parse_function_signatures(
|
||||
parser: &mut Parser,
|
||||
) -> Result<Vec<Signature>, SectionParsingError> {
|
||||
let mut signatures: Vec<Signature> = Vec::new();
|
||||
loop {
|
||||
match *parser.read() {
|
||||
@@ -36,28 +37,22 @@ pub fn parse_function_signatures(parser: &mut Parser)
|
||||
ref returns,
|
||||
}) => {
|
||||
let mut sig = Signature::new(CallConv::Native);
|
||||
sig.argument_types
|
||||
.extend(params
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
let cret_arg: cretonne::ir::Type = match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("only numeric types are supported in\
|
||||
sig.argument_types.extend(params.iter().map(|ty| {
|
||||
let cret_arg: cretonne::ir::Type = match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("only numeric types are supported in\
|
||||
function signatures"),
|
||||
};
|
||||
ArgumentType::new(cret_arg)
|
||||
}));
|
||||
sig.return_types
|
||||
.extend(returns
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
let cret_arg: cretonne::ir::Type = match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("only numeric types are supported in\
|
||||
};
|
||||
ArgumentType::new(cret_arg)
|
||||
}));
|
||||
sig.return_types.extend(returns.iter().map(|ty| {
|
||||
let cret_arg: cretonne::ir::Type = match type_to_type(ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(()) => panic!("only numeric types are supported in\
|
||||
function signatures"),
|
||||
};
|
||||
ArgumentType::new(cret_arg)
|
||||
}));
|
||||
};
|
||||
ArgumentType::new(cret_arg)
|
||||
}));
|
||||
signatures.push(sig);
|
||||
}
|
||||
ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))),
|
||||
@@ -78,30 +73,30 @@ pub fn parse_import_section(parser: &mut Parser) -> Result<Vec<Import>, SectionP
|
||||
ty: ImportSectionEntryType::Memory(MemoryType { limits: ref memlimits }), ..
|
||||
} => {
|
||||
imports.push(Import::Memory(Memory {
|
||||
pages_count: memlimits.initial as usize,
|
||||
maximum: memlimits.maximum.map(|x| x as usize),
|
||||
}))
|
||||
pages_count: memlimits.initial as usize,
|
||||
maximum: memlimits.maximum.map(|x| x as usize),
|
||||
}))
|
||||
}
|
||||
ParserState::ImportSectionEntry {
|
||||
ty: ImportSectionEntryType::Global(ref ty), ..
|
||||
} => {
|
||||
imports.push(Import::Global(Global {
|
||||
ty: type_to_type(&ty.content_type).unwrap(),
|
||||
mutability: ty.mutability != 0,
|
||||
initializer: GlobalInit::Import(),
|
||||
}));
|
||||
ty: type_to_type(&ty.content_type).unwrap(),
|
||||
mutability: ty.mutability != 0,
|
||||
initializer: GlobalInit::Import(),
|
||||
}));
|
||||
}
|
||||
ParserState::ImportSectionEntry {
|
||||
ty: ImportSectionEntryType::Table(ref tab), ..
|
||||
} => {
|
||||
imports.push(Import::Table(Table {
|
||||
ty: match type_to_type(&tab.element_type) {
|
||||
Ok(t) => TableElementType::Val(t),
|
||||
Err(()) => TableElementType::Func(),
|
||||
},
|
||||
size: tab.limits.initial as usize,
|
||||
maximum: tab.limits.maximum.map(|x| x as usize),
|
||||
}));
|
||||
ty: match type_to_type(&tab.element_type) {
|
||||
Ok(t) => TableElementType::Val(t),
|
||||
Err(()) => TableElementType::Func(),
|
||||
},
|
||||
size: tab.limits.initial as usize,
|
||||
maximum: tab.limits.maximum.map(|x| x as usize),
|
||||
}));
|
||||
}
|
||||
ParserState::EndSection => break,
|
||||
ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))),
|
||||
@@ -111,8 +106,9 @@ pub fn parse_import_section(parser: &mut Parser) -> Result<Vec<Import>, SectionP
|
||||
}
|
||||
|
||||
/// Retrieves the correspondances between functions and signatures from the function section
|
||||
pub fn parse_function_section(parser: &mut Parser)
|
||||
-> Result<Vec<SignatureIndex>, SectionParsingError> {
|
||||
pub fn parse_function_section(
|
||||
parser: &mut Parser,
|
||||
) -> Result<Vec<SignatureIndex>, SectionParsingError> {
|
||||
let mut funcs = Vec::new();
|
||||
loop {
|
||||
match *parser.read() {
|
||||
@@ -125,8 +121,9 @@ pub fn parse_function_section(parser: &mut Parser)
|
||||
}
|
||||
|
||||
/// Retrieves the names of the functions from the export section
|
||||
pub fn parse_export_section(parser: &mut Parser)
|
||||
-> Result<HashMap<FunctionIndex, String>, SectionParsingError> {
|
||||
pub fn parse_export_section(
|
||||
parser: &mut Parser,
|
||||
) -> Result<HashMap<FunctionIndex, String>, SectionParsingError> {
|
||||
let mut exports: HashMap<FunctionIndex, String> = HashMap::new();
|
||||
loop {
|
||||
match *parser.read() {
|
||||
@@ -137,8 +134,10 @@ pub fn parse_export_section(parser: &mut Parser)
|
||||
} => {
|
||||
match kind {
|
||||
&ExternalKind::Function => {
|
||||
exports.insert(index as FunctionIndex,
|
||||
String::from(from_utf8(field).unwrap()));
|
||||
exports.insert(
|
||||
index as FunctionIndex,
|
||||
String::from(from_utf8(field).unwrap()),
|
||||
);
|
||||
}
|
||||
_ => (),//TODO: deal with other kind of exports
|
||||
}
|
||||
@@ -157,9 +156,9 @@ pub fn parse_memory_section(parser: &mut Parser) -> Result<Vec<Memory>, SectionP
|
||||
match *parser.read() {
|
||||
ParserState::MemorySectionEntry(ref ty) => {
|
||||
memories.push(Memory {
|
||||
pages_count: ty.limits.initial as usize,
|
||||
maximum: ty.limits.maximum.map(|x| x as usize),
|
||||
})
|
||||
pages_count: ty.limits.initial as usize,
|
||||
maximum: ty.limits.maximum.map(|x| x as usize),
|
||||
})
|
||||
}
|
||||
ParserState::EndSection => break,
|
||||
ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))),
|
||||
@@ -169,9 +168,10 @@ pub fn parse_memory_section(parser: &mut Parser) -> Result<Vec<Memory>, SectionP
|
||||
}
|
||||
|
||||
/// Retrieves the size and maximum fields of memories from the memory section
|
||||
pub fn parse_global_section(parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime)
|
||||
-> Result<Vec<Global>, SectionParsingError> {
|
||||
pub fn parse_global_section(
|
||||
parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
) -> Result<Vec<Global>, SectionParsingError> {
|
||||
let mut globals = Vec::new();
|
||||
loop {
|
||||
let (content_type, mutability) = match *parser.read() {
|
||||
@@ -221,10 +221,11 @@ pub fn parse_global_section(parser: &mut Parser,
|
||||
Ok(globals)
|
||||
}
|
||||
|
||||
pub fn parse_data_section(parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
globals: &[Global])
|
||||
-> Result<(), SectionParsingError> {
|
||||
pub fn parse_data_section(
|
||||
parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
globals: &[Global],
|
||||
) -> Result<(), SectionParsingError> {
|
||||
loop {
|
||||
let memory_index = match *parser.read() {
|
||||
ParserState::BeginDataSectionEntry(memory_index) => memory_index,
|
||||
@@ -238,8 +239,10 @@ pub fn parse_data_section(parser: &mut Parser,
|
||||
let offset = match *parser.read() {
|
||||
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
||||
if value < 0 {
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from("negative \
|
||||
offset value")));
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from(
|
||||
"negative \
|
||||
offset value",
|
||||
)));
|
||||
} else {
|
||||
value as usize
|
||||
}
|
||||
@@ -248,15 +251,19 @@ pub fn parse_data_section(parser: &mut Parser,
|
||||
match globals[global_index as usize].initializer {
|
||||
GlobalInit::I32Const(value) => {
|
||||
if value < 0 {
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from("\
|
||||
negative offset value")));
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from(
|
||||
"\
|
||||
negative offset value",
|
||||
)));
|
||||
} else {
|
||||
value as usize
|
||||
}
|
||||
}
|
||||
GlobalInit::Import() => {
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from("\
|
||||
imported globals not supported")))
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from(
|
||||
"\
|
||||
imported globals not supported",
|
||||
)))
|
||||
} // TODO: add runtime support
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
@@ -288,20 +295,21 @@ pub fn parse_data_section(parser: &mut Parser,
|
||||
}
|
||||
|
||||
/// Retrieves the tables from the table section
|
||||
pub fn parse_table_section(parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime)
|
||||
-> Result<(), SectionParsingError> {
|
||||
pub fn parse_table_section(
|
||||
parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
) -> Result<(), SectionParsingError> {
|
||||
loop {
|
||||
match *parser.read() {
|
||||
ParserState::TableSectionEntry(ref table) => {
|
||||
runtime.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),
|
||||
})
|
||||
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,
|
||||
ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))),
|
||||
@@ -311,10 +319,11 @@ pub fn parse_table_section(parser: &mut Parser,
|
||||
}
|
||||
|
||||
/// Retrieves the tables from the table section
|
||||
pub fn parse_elements_section(parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
globals: &[Global])
|
||||
-> Result<(), SectionParsingError> {
|
||||
pub fn parse_elements_section(
|
||||
parser: &mut Parser,
|
||||
runtime: &mut WasmRuntime,
|
||||
globals: &[Global],
|
||||
) -> Result<(), SectionParsingError> {
|
||||
loop {
|
||||
let table_index = match *parser.read() {
|
||||
ParserState::BeginElementSectionEntry(ref table_index) => *table_index as TableIndex,
|
||||
@@ -328,8 +337,10 @@ pub fn parse_elements_section(parser: &mut Parser,
|
||||
let offset = match *parser.read() {
|
||||
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
||||
if value < 0 {
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from("negative \
|
||||
offset value")));
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from(
|
||||
"negative \
|
||||
offset value",
|
||||
)));
|
||||
} else {
|
||||
value as usize
|
||||
}
|
||||
@@ -338,8 +349,10 @@ pub fn parse_elements_section(parser: &mut Parser,
|
||||
match globals[global_index as usize].initializer {
|
||||
GlobalInit::I32Const(value) => {
|
||||
if value < 0 {
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from("\
|
||||
negative offset value")));
|
||||
return Err(SectionParsingError::WrongSectionContent(String::from(
|
||||
"\
|
||||
negative offset value",
|
||||
)));
|
||||
} else {
|
||||
value as usize
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub type RawByte = u8;
|
||||
pub type MemoryAddress = usize;
|
||||
|
||||
/// WebAssembly import.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Import {
|
||||
Function { sig_index: u32 },
|
||||
Memory(Memory),
|
||||
@@ -30,7 +30,7 @@ pub enum Import {
|
||||
}
|
||||
|
||||
/// WebAssembly global.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Global {
|
||||
/// The type of the value stored in the global.
|
||||
pub ty: cretonne::ir::Type,
|
||||
@@ -41,7 +41,7 @@ pub struct Global {
|
||||
}
|
||||
|
||||
/// Globals are initialized via the four `const` operators or by referring to another import.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GlobalInit {
|
||||
/// An `i32.const`.
|
||||
I32Const(i32),
|
||||
@@ -58,7 +58,7 @@ pub enum GlobalInit {
|
||||
}
|
||||
|
||||
/// WebAssembly table.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Table {
|
||||
/// The type of data stored in elements of the table.
|
||||
pub ty: TableElementType,
|
||||
@@ -69,14 +69,14 @@ pub struct Table {
|
||||
}
|
||||
|
||||
/// WebAssembly table element. Can be a function or a scalar type.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TableElementType {
|
||||
Val(cretonne::ir::Type),
|
||||
Func(),
|
||||
}
|
||||
|
||||
/// WebAssembly linear memory.
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Memory {
|
||||
/// The minimum number of pages in the memory.
|
||||
pub pages_count: usize,
|
||||
@@ -139,8 +139,9 @@ pub fn translate_type(ty: wasmparser::Type) -> Result<Vec<cretonne::ir::Type>, (
|
||||
/// Inverts the key-value relation in the imports hashmap. Indeed, these hashmaps are built by
|
||||
/// feeding the function indexes in the module but are used by the runtime with the `FuncRef` as
|
||||
/// keys.
|
||||
pub fn invert_hashmaps(imports: code_translator::FunctionImports)
|
||||
-> module_translator::ImportMappings {
|
||||
pub fn invert_hashmaps(
|
||||
imports: code_translator::FunctionImports,
|
||||
) -> module_translator::ImportMappings {
|
||||
let mut new_imports = module_translator::ImportMappings::new();
|
||||
for (func_index, func_ref) in &imports.functions {
|
||||
new_imports.functions.insert(*func_ref, *func_index);
|
||||
|
||||
Reference in New Issue
Block a user