1327: fix(interface-types) Stack pops items in the same order than Wasm invocation rule r=Hywan a=Hywan

This PR fixes the items order when popped from the stack. It matches [the Wasm invocation rule](https://webassembly.github.io/spec/core/exec/instructions.html#invocation-of-function-address).

Not that it is more performant in our case, since we use the `drain` API rather than `drain` + `rev`.

Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
This commit is contained in:
bors[bot] 2020-03-24 09:49:42 +00:00 committed by GitHub
commit 56aec04d1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 22 deletions

View File

@ -70,8 +70,8 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_call_core = test_call_core =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 }, Instruction::CallCore { function_index: 42 },
], ],
invocation_inputs: [ invocation_inputs: [
@ -113,8 +113,8 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_call_core__invalid_types_in_the_stack = test_call_core__invalid_types_in_the_stack =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 }, Instruction::CallCore { function_index: 42 },
], ],
invocation_inputs: [ invocation_inputs: [
@ -129,8 +129,8 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_call_core__failure_when_calling = test_call_core__failure_when_calling =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 }, Instruction::CallCore { function_index: 42 },
], ],
invocation_inputs: [ invocation_inputs: [
@ -160,8 +160,8 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_call_core__void = test_call_core__void =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 }, Instruction::CallCore { function_index: 42 },
], ],
invocation_inputs: [ invocation_inputs: [

View File

@ -27,8 +27,8 @@ executable_instruction!(
) )
})?; })?;
let length = to_native::<i32>(&inputs[0], instruction)? as usize; let pointer = to_native::<i32>(&inputs[0], instruction)? as usize;
let pointer = to_native::<i32>(&inputs[1], instruction)? as usize; let length = to_native::<i32>(&inputs[1], instruction)? as usize;
let memory_view = memory.view(); let memory_view = memory.view();
if length == 0 { if length == 0 {
@ -67,15 +67,15 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_memory_to_string = test_memory_to_string =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::MemoryToString, Instruction::MemoryToString,
], ],
invocation_inputs: [ invocation_inputs: [
InterfaceValue::I32(13),
// ^^^^^^^ length
InterfaceValue::I32(0), InterfaceValue::I32(0),
// ^^^^^^ pointer // ^^^^^^ pointer
InterfaceValue::I32(13),
// ^^^^^^^ length
], ],
instance: Instance { instance: Instance {
memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
@ -87,8 +87,8 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_memory_to_string__empty_string = test_memory_to_string__empty_string =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::MemoryToString, Instruction::MemoryToString,
], ],
invocation_inputs: [ invocation_inputs: [
@ -105,15 +105,15 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_memory_to_string__read_out_of_memory = test_memory_to_string__read_out_of_memory =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::MemoryToString, Instruction::MemoryToString,
], ],
invocation_inputs: [ invocation_inputs: [
InterfaceValue::I32(13),
// ^^^^^^^ length is too long
InterfaceValue::I32(0), InterfaceValue::I32(0),
// ^^^^^^ pointer // ^^^^^^ pointer
InterfaceValue::I32(13),
// ^^^^^^^ length is too long
], ],
instance: Instance { instance: Instance {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
@ -125,15 +125,15 @@ mod tests {
test_executable_instruction!( test_executable_instruction!(
test_memory_to_string__invalid_encoding = test_memory_to_string__invalid_encoding =
instructions: [ instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 }, Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::MemoryToString, Instruction::MemoryToString,
], ],
invocation_inputs: [ invocation_inputs: [
InterfaceValue::I32(4),
// ^^^^^^ length is too long
InterfaceValue::I32(0), InterfaceValue::I32(0),
// ^^^^^^ pointer // ^^^^^^ pointer
InterfaceValue::I32(4),
// ^^^^^^ length is too long
], ],
instance: Instance { instance: Instance {
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()), memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
@ -150,8 +150,8 @@ mod tests {
// ^^^^^^^^^^^^^^ `memory-to-string` expects 2 values on the stack, only one is present. // ^^^^^^^^^^^^^^ `memory-to-string` expects 2 values on the stack, only one is present.
], ],
invocation_inputs: [ invocation_inputs: [
InterfaceValue::I32(13),
InterfaceValue::I32(0), InterfaceValue::I32(0),
InterfaceValue::I32(13),
], ],
instance: Instance::new(), instance: Instance::new(),
error: r#"`memory-to-string` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#, error: r#"`memory-to-string` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,

View File

@ -22,8 +22,8 @@ pub trait Stackable {
/// Removes `n` elements from the end of the stack, `None` if the /// Removes `n` elements from the end of the stack, `None` if the
/// stack doesn't contain enough elements. /// stack doesn't contain enough elements.
/// Returned items are ordered by FIFO: the last element comes /// Returned items are in reverse order: the last element comes
/// first in the list. /// last in the list.
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>>; fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>>;
} }
@ -78,7 +78,6 @@ where
let items = self let items = self
.inner .inner
.drain(self.inner.len() - n..) .drain(self.inner.len() - n..)
.rev()
.collect::<Vec<Self::Item>>(); .collect::<Vec<Self::Item>>();
assert!(items.len() == n); assert!(items.len() == n);
@ -121,9 +120,9 @@ mod tests {
stack.push(6); stack.push(6);
assert_eq!(stack.pop(1), Some(vec![6])); assert_eq!(stack.pop(1), Some(vec![6]));
assert_eq!(stack.pop(2), Some(vec![5, 4])); assert_eq!(stack.pop(2), Some(vec![4, 5]));
assert_eq!(stack.pop(4), None); // not enough items assert_eq!(stack.pop(4), None); // not enough items
assert_eq!(stack.pop(3), Some(vec![3, 2, 1])); assert_eq!(stack.pop(3), Some(vec![1, 2, 3]));
assert_eq!(stack.pop1(), None); assert_eq!(stack.pop1(), None);
assert_eq!(stack.is_empty(), true); assert_eq!(stack.is_empty(), true);
} }