feat(interface-types) Implement the string.size instruction.

This commit is contained in:
Ivan Enderlin
2020-03-24 15:33:42 +01:00
parent daef7b0bfd
commit 38f62392ff
7 changed files with 84 additions and 2 deletions

View File

@@ -219,6 +219,8 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
) )
} }
0x24 => (input, Instruction::StringSize),
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
}) })
} }
@@ -620,7 +622,7 @@ mod tests {
#[test] #[test]
fn test_instructions() { fn test_instructions() {
let input = &[ let input = &[
0x24, // list of 36 items 0x25, // list of 37 items
0x00, 0x01, // ArgumentGet { index: 1 } 0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 } 0x01, 0x01, // CallCore { function_index: 1 }
0x02, // S8FromI32 0x02, // S8FromI32
@@ -657,6 +659,7 @@ mod tests {
0x21, // I64FromU64 0x21, // I64FromU64
0x22, // StringLiftMemory 0x22, // StringLiftMemory
0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x23, 0x01, // StringLowerMemory { allocator_index: 1 }
0x24, // StringSize
0x0a, 0x0a,
]; ];
let output = Ok(( let output = Ok((
@@ -698,6 +701,7 @@ mod tests {
Instruction::I64FromU64, Instruction::I64FromU64,
Instruction::StringLiftMemory, Instruction::StringLiftMemory,
Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringLowerMemory { allocator_index: 1 },
Instruction::StringSize,
], ],
)); ));

View File

@@ -62,6 +62,7 @@ mod keyword {
custom_keyword!(i64_from_u64 = "i64.from_u64"); custom_keyword!(i64_from_u64 = "i64.from_u64");
custom_keyword!(string_lift_memory = "string.lift_memory"); custom_keyword!(string_lift_memory = "string.lift_memory");
custom_keyword!(string_lower_memory = "string.lower_memory"); custom_keyword!(string_lower_memory = "string.lower_memory");
custom_keyword!(string_size = "string.size");
} }
impl Parse<'_> for InterfaceType { impl Parse<'_> for InterfaceType {
@@ -285,6 +286,10 @@ impl<'a> Parse<'a> for Instruction {
Ok(Instruction::StringLowerMemory { Ok(Instruction::StringLowerMemory {
allocator_index: parser.parse()?, allocator_index: parser.parse()?,
}) })
} else if lookahead.peek::<keyword::string_size>() {
parser.parse::<keyword::string_size>()?;
Ok(Instruction::StringSize)
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@@ -666,6 +671,7 @@ mod tests {
"i64.from_u64", "i64.from_u64",
"string.lift_memory", "string.lift_memory",
"string.lower_memory 42", "string.lower_memory 42",
"string.size",
]; ];
let outputs = vec![ let outputs = vec![
Instruction::ArgumentGet { index: 7 }, Instruction::ArgumentGet { index: 7 },
@@ -706,6 +712,7 @@ mod tests {
Instruction::StringLowerMemory { Instruction::StringLowerMemory {
allocator_index: 42, allocator_index: 42,
}, },
Instruction::StringSize,
]; ];
assert_eq!(inputs.len(), outputs.len()); assert_eq!(inputs.len(), outputs.len());

View File

@@ -299,6 +299,8 @@ where
0x23_u8.to_bytes(writer)?; 0x23_u8.to_bytes(writer)?;
(*allocator_index as u64).to_bytes(writer)?; (*allocator_index as u64).to_bytes(writer)?;
} }
Instruction::StringSize => 0x24_u8.to_bytes(writer)?,
} }
Ok(()) Ok(())
@@ -577,9 +579,10 @@ mod tests {
Instruction::I64FromU64, Instruction::I64FromU64,
Instruction::StringLiftMemory, Instruction::StringLiftMemory,
Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringLowerMemory { allocator_index: 1 },
Instruction::StringSize,
], ],
&[ &[
0x24, // list of 36 items 0x25, // list of 37 items
0x00, 0x01, // ArgumentGet { index: 1 } 0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 } 0x01, 0x01, // CallCore { function_index: 1 }
0x02, // S8FromI32 0x02, // S8FromI32
@@ -616,6 +619,7 @@ mod tests {
0x21, // I64FromU64 0x21, // I64FromU64
0x22, // StringLiftMemory 0x22, // StringLiftMemory
0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x23, 0x01, // StringLowerMemory { allocator_index: 1 }
0x24, // StringSize
] ]
); );
} }

View File

@@ -121,6 +121,7 @@ impl ToString for &Instruction {
Instruction::StringLowerMemory { allocator_index } => { Instruction::StringLowerMemory { allocator_index } => {
format!(r#"string.lower_memory {}"#, allocator_index) format!(r#"string.lower_memory {}"#, allocator_index)
} }
Instruction::StringSize => "string.size".into(),
} }
} }
} }
@@ -391,6 +392,7 @@ mod tests {
allocator_index: 42, allocator_index: 42,
}) })
.to_string(), .to_string(),
(&Instruction::StringSize).to_string(),
]; ];
let outputs = vec![ let outputs = vec![
"arg.get 7", "arg.get 7",
@@ -429,6 +431,7 @@ mod tests {
"i64.from_u64", "i64.from_u64",
"string.lift_memory", "string.lift_memory",
"string.lower_memory 42", "string.lower_memory 42",
"string.size",
]; ];
assert_eq!(inputs, outputs); assert_eq!(inputs, outputs);

View File

@@ -119,4 +119,7 @@ pub enum Instruction {
/// The allocator function index. /// The allocator function index.
allocator_index: u32, allocator_index: u32,
}, },
/// The `string.size` instruction.
StringSize,
} }

View File

@@ -135,6 +135,34 @@ executable_instruction!(
} }
); );
executable_instruction!(
string_size(instruction: Instruction) -> _ {
move |runtime| -> _ {
let value = runtime.stack.peek1().ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 1 },
)
})?;
if let InterfaceValue::String(string) = value {
let length = string.len() as i32;
runtime.stack.push(InterfaceValue::I32(length));
Ok(())
} else {
Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidValueOnTheStack {
expected_type: InterfaceType::String,
received_type: value.into(),
}
))
}
}
}
);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
test_executable_instruction!( test_executable_instruction!(
@@ -323,4 +351,36 @@ mod tests {
}, },
error: r#"`string.lower_memory 153` the local or import function `153` has the signature `[I32] -> [I32]` but it received values of kind `[I32, I32] -> []`"#, error: r#"`string.lower_memory 153` the local or import function `153` has the signature `[I32] -> [I32]` but it received values of kind `[I32, I32] -> []`"#,
); );
test_executable_instruction!(
test_string_size =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
],
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
instance: Instance::new(),
stack: [InterfaceValue::String("Hello, World!".into()), InterfaceValue::I32(13)],
);
test_executable_instruction!(
test_string_size__stack_is_too_small =
instructions: [
Instruction::StringSize,
],
invocation_inputs: [],
instance: Instance::new(),
error: r#"`string.size` needed to read `1` value(s) from the stack, but it doesn't contain enough data"#,
);
test_executable_instruction!(
test_string_size__invalid_value_on_the_stack =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
error: r#"`string.size` read a value of type `I32` from the stack, but the type `String` was expected"#,
);
} }

View File

@@ -234,6 +234,7 @@ where
Instruction::StringLowerMemory { allocator_index } => { Instruction::StringLowerMemory { allocator_index } => {
instructions::string_lower_memory(*allocator_index, *instruction) instructions::string_lower_memory(*allocator_index, *instruction)
} }
Instruction::StringSize => instructions::string_size(*instruction),
}) })
.collect(); .collect();