diff --git a/Cargo.toml b/Cargo.toml index 57eb07ee62bbff..b3c7a1fcfedec5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -239,7 +239,6 @@ members = [ "third_party/move/move-vm/transactional-tests", "third_party/move/move-vm/types", "third_party/move/testing-infra/module-generation", - "third_party/move/testing-infra/test-generation", "third_party/move/testing-infra/transactional-test-runner", "third_party/move/tools/move-bytecode-utils", "third_party/move/tools/move-bytecode-viewer", diff --git a/third_party/move/testing-infra/test-generation/Cargo.toml b/third_party/move/testing-infra/test-generation/Cargo.toml deleted file mode 100644 index 5989986af868a2..00000000000000 --- a/third_party/move/testing-infra/test-generation/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "test-generation" -version = "0.1.0" -authors = ["Diem Association "] -description = "Tool for generating tests for the bytecode verifier and Move VM runtime" -repository = "https://github.com/diem/diem" -homepage = "https://diem.com" -license = "Apache-2.0" -publish = false -edition = "2021" - -[dependencies] -clap = { workspace = true, features = ["derive"] } -crossbeam-channel = { workspace = true } -getrandom = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -module-generation = { path = "../module-generation" } -num_cpus = { workspace = true } -# Cannot use workspace version as aptos-core currently cannot be upgraded -# to newer rand. See https://github.com/aptos-labs/aptos-core/issues/13031 -rand = { version = "0.8.5" } -tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter"] } - -move-binary-format = { path = "../../move-binary-format" } -move-bytecode-verifier = { path = "../../move-bytecode-verifier" } -move-compiler = { path = "../../move-compiler" } -move-core-types = { path = "../../move-core/types" } -move-stdlib = { path = "../../move-stdlib" } -move-vm-runtime = { path = "../../move-vm/runtime" } -move-vm-test-utils = { path = "../../move-vm/test-utils" } -move-vm-types = { path = "../../move-vm/types" } diff --git a/third_party/move/testing-infra/test-generation/README.md b/third_party/move/testing-infra/test-generation/README.md deleted file mode 100644 index 547ea2fe268716..00000000000000 --- a/third_party/move/testing-infra/test-generation/README.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -id: bytecode-test-generation -title: Bytecode Test Generation Tool -custom_edit_url: https://github.com/move-language/move/edit/main/language/testing-infra/test-generation/README.md ---- - -# Bytecode Test Generation Tool - -## Overview - -This tool can generate known-valid or known-invalid Move bytecode programs. -Known-valid means that the program is valid by construction; it was constructed -according to a formal specification of the Move bytecode language. Known-invalid -bytecode programs are those that diverge from the specification in a controlled way. - -The generated modules are checked by the Move bytecode verifier and then run on the VM runtime. -If the module contains known-valid bytecode, we should expect that the program will -pass the bytecode verifier. If it does not pass the verifier then this indicates that either -there is a bug in the bytecode verifier or the formal specification of the failing -instruction is incorrect. Likewise, it should also run successfully on the VM runtime. - -If the module contains known-invalid bytecode, we should expect that the verifier will -reject the module. If the verifier does not reject the module this indicates again -that either there is a bug in the bytecode verifier or the formal specification is -incorrect. Likewise for the VM runtime. - -## Usage - -### Building and Configuration - -To build the tool -- `cargo build` - -There are configuration options (and explanations for their behavior) in `src/config.rs`. -Most of the time these should be left as the provided defaults. - -### Running - -The tool expects up to two arguments: -- `--iterations`: The number of programs that should be generated and tested -- `--output`: (Optional) If provided, this is the path to which modules that result in errors will be serialied and saved. If not provided, failing cases will be logged to the console. - -Additionally, there is an optional flag `RUST_LOG` that controls the verbosity of debug -information. It goes from `error`, the least information, to `debug` the most information. -The most common setting for this flag is `info` which will print stats such as the number -of iterations, the number of verified and executed programs, etc. - -To run the tool -- `RUST_LOG=info cargo run -- --iterations N --output PATH` - -## Architecture - -This tool works by modeling the state of the VM abstractly, and by modeling the bytecode -instructions in terms of that abstract state. The abstract state is defined in -`abstact_state.rs`. It consists of type-level modeling of the VM stack, locals, and borrow -graph. - -Instructions are defined in terms of their preconditions and effects. These definitions are -found in `summaries.rs` and use macros defined in `transitions.rs`. The preconditions of -an instruction are predicates that are true or false for a given abstract state. For example -the `Bytecode::Add` instruction requires the stack to contain two integers. This is modeled -by saying that the preconditions of `Bytecode::Add` are -- `state_stack_has!(0, Some(SignatureToken::U64))` -- `state_stack_has!(1, Some(SignatureToken::U64))` -where indexes 0 and 1 refer to the top two elements of the stack. - -The effects of an instruction describe how the instruction modifies the abstract state. For -`Bytecode::Add` the effects are that it performs two pops on the stack and pushes a -`SignatureToken::U64` to the stack. - -In this way, we are able to fully capture what each instruction needs and does. -This information is used to generate valid bytecode programs. - -Generation of bytecode programs proceeds as follows: -1. In `lib.rs` the generator loop begins by initializing a `ModuleBuilder` -2. The `ModuleBuilder`, defined in `../utils/src/module_generator.rs`, generates a module definition -3. The `ModuleBuilder` calls the `generator` defined in `bytecode_generator.rs` to fill in function bodies within the module -4. The `generator` builds a control flow graph (CFG) in `control_flow_graph.rs`. Each block of the CFG is assigned a valid starting and ending abstract state. -5. The `generator` fills in blocks of the CFG according to the following algorithm: - 1. Given starting abstract state `AS1`, let `candidates` be the list of all instructions whose preconditions are all satisfied in `AS1` - - If invalid generation is desired, then let `x` preconditions be `false` - 2. Select candidate `instr` from `candidates` according to the stack height heuristic - - The stack height heuristic selects instructions that add to the stack when the height is small, and instructions that subtract from the stack when the height is large - 3. Apply the effects of `instr` to `AS1`, producing `AS2` - 4. If the stack is empty, terminate, otherwise repeat from step a with `AS2` - -This results in the generation of one module. The module is then given to the bytecode -verifier and the bytecode verifier's behavior (i.e. no verification errors, some verification -errors, panic, crash) is recorded. Likewise with the VM runtime (except with runtime errors -rather than verification errors). Depending on the configuration that this tool is built -with, if the module caused a verification error, panic, or crash the module will then be -printed out or serialized to disk. - -This will continue for the number of iterations specified when invoking the tool. - -Other files: -- `error.rs` defines an error struct which is used to pass error messages. -- `tests/` contains a set of files that test the preconditions and effects of each bytecode instruction - -## Extending the tool - -The most common change or extension to this tool will probably be changing instruction -preconditions and effects. To do that follow these steps: -1. See if there already a macro defined in `transitions.rs` that captures your desired precondition/effect -2. If the macro is already defined, just add it to the summary of the instruction being changed in `summaries.rs` -3. If a suitable macro does not exist, define it in `transitions.rs`. Look at other macros in that file for examples. -4. Macros in `transitions.rs` have access to the public fields and functions of the `AbstractState`. If your macro needs access to something more, add a new helper method in `abstract_state.rs` and then invoke it in the macro. diff --git a/third_party/move/testing-infra/test-generation/measure-coverage.sh b/third_party/move/testing-infra/test-generation/measure-coverage.sh deleted file mode 100755 index 09738f8bc7b87e..00000000000000 --- a/third_party/move/testing-infra/test-generation/measure-coverage.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash -# Copyright (c) The Diem Core Contributors -# Copyright (c) The Move Contributors -# SPDX-License-Identifier: Apache-2.0 - -# Check that the test directory and report path arguments are provided -if [ $# -lt 1 ] -then - echo "Usage: $0 [--batch]" - echo "The resulting coverage report will be stored in ." - echo "--batch will skip all prompts." - exit 1 -fi - -# User prompts will be skipped if '--batch' is given as the third argument -SKIP_PROMPTS=0 -if [ $# -eq 2 -a "$2" == "--batch" ] -then - SKIP_PROMPTS=1 -fi - -# Set the directory to which the report will be saved -COVERAGE_DIR=$1 - -# This needs to run in test-generation -TOOL_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" -if [ $(pwd) != $TOOL_DIR ] -then - echo "Error: This needs to run from test-generation/, not in $(pwd)" >&2 - exit 1 -fi - -set -e - -# Check that grcov is installed -if ! [ -x "$(command -v grcov)" ]; then - echo "Error: grcov is not installed." >&2 - if [ $SKIP_PROMPTS -eq 0 ] - then - read -p "Install grcov? [yY/*] " -n 1 -r - echo "" - if [[ ! $REPLY =~ ^[Yy]$ ]] - then - [[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 - fi - cargo install grcov - else - exit 1 - fi -fi - -# Check that lcov is installed -if ! [ -x "$(command -v lcov)" ]; then - echo "Error: lcov is not installed." >&2 - echo "Documentation for lcov can be found at http://ltp.sourceforge.net/coverage/lcov.php" - echo "If on macOS and using homebrew, run 'brew install lcov'" - exit 1 -fi - -# Warn that cargo clean will happen -if [ $SKIP_PROMPTS -eq 0 ] -then - read -p "Generate coverage report? This will run cargo clean. [yY/*] " -n 1 -r - echo "" - if [[ ! $REPLY =~ ^[Yy]$ ]] - then - [[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 - fi -fi - -# Set flags for coverage generation -export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Coverflow-checks=off -Zno-landing-pads" -export CARGO_INCREMENTAL=0 - -# Clean the project -echo "Cleaning project..." -cargo clean - -# Run bytecode test generator -echo "Running bytecode test generator..." -RUST_LOG=info cargo run -- --iterations 1000 - -# Make the coverage directory if it doesn't exist -if [ ! -d $COVERAGE_DIR ]; then - mkdir $COVERAGE_DIR; -fi - -# Generate lcov report -echo "Generating lcov report at ${COVERAGE_DIR}/lcov.info..." -grcov $TOOL_DIR/../../../target/ -t lcov --llvm --branch --ignore-dir "/*" -o $COVERAGE_DIR/lcov.info - -# Generate HTML report -echo "Generating report at ${COVERAGE_DIR}..." -# Flag "--ignore-errors source" ignores missing source files -(cd $TOOL_DIR/../../../; genhtml -o $COVERAGE_DIR --show-details --highlight --ignore-errors source --legend --branch-coverage $COVERAGE_DIR/lcov.info) - -echo "Done. Please view report at ${COVERAGE_DIR}/index.html" diff --git a/third_party/move/testing-infra/test-generation/src/abstract_state.rs b/third_party/move/testing-infra/test-generation/src/abstract_state.rs deleted file mode 100644 index 39e2ec6b5a1ed3..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/abstract_state.rs +++ /dev/null @@ -1,719 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::{borrow_graph::BorrowGraph, error::VMError}; -use move_binary_format::{ - access::ModuleAccess, - file_format::{ - empty_module, Ability, AbilitySet, CompiledModule, FieldInstantiation, - FieldInstantiationIndex, FunctionHandleIndex, FunctionInstantiation, - FunctionInstantiationIndex, Signature, SignatureIndex, SignatureToken, - StructDefInstantiation, StructDefInstantiationIndex, StructDefinitionIndex, TableIndex, - }, -}; -use std::{ - collections::{HashMap, HashSet}, - fmt, -}; - -/// The BorrowState denotes whether a local is `Available` or -/// has been moved and is `Unavailable`. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BorrowState { - Available, - Unavailable, -} - -/// This models a value on the stack or in the locals -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AbstractValue { - /// Represents the type of the value - pub token: SignatureToken, - - /// Represents the abilities of the value - pub abilities: AbilitySet, -} - -/// This models the mutability of a reference -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Mutability { - /// Represents a mutable reference - Mutable, - - /// Represents an immutable reference - Immutable, - - /// When we don't need to specify whether - /// the reference is mutable or immutable - Either, -} - -impl AbstractValue { - /// Create a new primitive `AbstractValue` given its type; the kind will be `Copyable` - pub fn new_primitive(token: SignatureToken) -> AbstractValue { - assert!( - match token { - SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::TypeParameter(_) => false, - SignatureToken::Bool - | SignatureToken::Address - | SignatureToken::U8 - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::U256 => true, - }, - "AbstractValue::new_primitive must be applied with primitive type" - ); - AbstractValue { - token, - abilities: AbilitySet::PRIMITIVES, - } - } - - /// Create a new reference `AbstractValue` given its type and kind - pub fn new_reference(token: SignatureToken, abilities: AbilitySet) -> AbstractValue { - assert!( - matches!( - token, - SignatureToken::Reference(_) | SignatureToken::MutableReference(_) - ), - "AbstractValue::new_reference must be applied with a reference type" - ); - AbstractValue { token, abilities } - } - - /// Create a new struct `AbstractValue` given its type and kind - pub fn new_struct(token: SignatureToken, abilities: AbilitySet) -> AbstractValue { - assert!( - matches!(token, SignatureToken::Struct(_)), - "AbstractValue::new_struct must be applied with a struct type" - ); - AbstractValue { token, abilities } - } - - pub fn new_value(token: SignatureToken, abilities: AbilitySet) -> AbstractValue { - AbstractValue { token, abilities } - } - - /// Predicate on whether the type of the abstract value is generic -- it is if it contains a - /// type parameter. - pub fn is_generic(&self) -> bool { - Self::is_generic_token(&self.token) - } - - fn is_generic_token(token: &SignatureToken) -> bool { - match token { - SignatureToken::TypeParameter(_) => true, - SignatureToken::StructInstantiation(_, _) => true, - SignatureToken::Reference(tok) | SignatureToken::MutableReference(tok) => { - Self::is_generic_token(tok) - }, - _ => false, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CallGraph { - calls: HashMap>, - max_function_handle_index: usize, -} - -impl CallGraph { - pub fn new(max_function_handle_index: usize) -> Self { - Self { - calls: HashMap::new(), - max_function_handle_index, - } - } - - pub fn add_call(&mut self, caller: FunctionHandleIndex, callee: FunctionHandleIndex) { - self.calls.entry(caller).or_default().insert(callee); - } - - pub fn can_call(&self, my_index: FunctionHandleIndex) -> Vec { - // We want the set of function handles that don't lead to a recursive call-graph - (0..self.max_function_handle_index) - .filter(|index| { - self.call_depth(my_index, FunctionHandleIndex(*index as TableIndex)) - .is_some() - }) - .map(|i| FunctionHandleIndex(i as TableIndex)) - .collect() - } - - pub fn max_calling_depth(&self, index: FunctionHandleIndex) -> usize { - let mut instantiation_depth = 0; - for (caller, callees) in self.calls.iter() { - for callee in callees.iter() { - if *callee == index { - let depth = self.max_calling_depth(*caller) + 1; - instantiation_depth = std::cmp::max(depth, instantiation_depth); - } - } - } - instantiation_depth - } - - /// None if recursive, Some(index) if non-recursive, and index is the length of the maximal call - /// graph path originating at caller, and calling through callee. - pub fn call_depth( - &self, - caller: FunctionHandleIndex, - callee: FunctionHandleIndex, - ) -> Option { - if caller == callee { - return None; - } - match self.calls.get(&callee) { - None => Some(1), - Some(callee_callees) => { - if callee_callees.contains(&caller) { - return None; - } - let call_depths = callee_callees - .iter() - .filter_map(|callee_callee| self.call_depth(caller, *callee_callee)) - .collect::>(); - if call_depths.len() < callee_callees.len() { - // We found a recursive call - None - } else { - let max = call_depths.iter().max().unwrap(); - Some(max + 1) - } - }, - } - } -} - -/// During the generation of a bytecode sequence, specific instantiations may need to be made, that -/// may not yet exist in the underlying module. Instead of mutating the underlying module in order to record these instantiations in the -/// locals signature table, we instead build wrapper around the underlying module containing the -/// type instantiations, and at the end materialize this updated signature pool into a module. We -/// also need the ability to quickly determine if an instantiation has already been created, and if -/// so, at which index. So this also keeps a reverse lookup table of instantiation to -/// SignatureIndex. -#[derive(Debug, Clone)] -pub struct InstantiableModule { - // A reverse lookup table for instantiations. - sig_instance_for_offset: Vec>, - instantiations: HashMap, SignatureIndex>, - - struct_instance_for_offset: Vec, - struct_instantiations: HashMap, - - func_instance_for_offset: Vec, - function_instantiations: HashMap, - - field_instance_for_offset: Vec, - field_instantiations: HashMap, - - pub module: CompiledModule, -} - -impl InstantiableModule { - pub fn new(module: CompiledModule) -> Self { - Self { - instantiations: module - .signatures() - .iter() - .enumerate() - .map(|(index, sig)| (sig.0.clone(), SignatureIndex(index as TableIndex))) - .collect::>(), - sig_instance_for_offset: module - .signatures() - .iter() - .map(|loc_sig| loc_sig.0.clone()) - .collect(), - - struct_instantiations: module - .struct_instantiations() - .iter() - .enumerate() - .map(|(index, si)| (si.clone(), StructDefInstantiationIndex(index as TableIndex))) - .collect::>(), - struct_instance_for_offset: module.struct_instantiations().to_vec(), - - function_instantiations: module - .function_instantiations() - .iter() - .enumerate() - .map(|(index, fi)| (fi.clone(), FunctionInstantiationIndex(index as TableIndex))) - .collect::>(), - func_instance_for_offset: module.function_instantiations().to_vec(), - - field_instantiations: module - .field_instantiations() - .iter() - .enumerate() - .map(|(index, fi)| (fi.clone(), FieldInstantiationIndex(index as TableIndex))) - .collect::>(), - field_instance_for_offset: module.field_instantiations().to_vec(), - module, - } - } - - /// If the `instantiant` is not in the `instantiations` table, this adds the instantiant to the - /// `instance_for_offset` for table, and adds the index to the reverse lookup table. Returns - /// the SignatureIndex for the `instantiant`. - pub fn add_instantiation(&mut self, instantiant: Vec) -> SignatureIndex { - match self.instantiations.get(&instantiant) { - Some(index) => *index, - None => { - let current_index = - SignatureIndex(self.sig_instance_for_offset.len() as TableIndex); - self.instantiations - .insert(instantiant.clone(), current_index); - self.sig_instance_for_offset.push(instantiant); - current_index - }, - } - } - - /// If the `instantiant` is not in the `struct_instantiations` table, this adds the - /// instantiant to the `struct_instance_for_offset` for table, and adds the index to the - /// reverse lookup table. - /// Returns the SignatureIndex for the `instantiant`. - pub fn add_struct_instantiation( - &mut self, - instantiant: StructDefInstantiation, - ) -> StructDefInstantiationIndex { - match self.struct_instantiations.get(&instantiant) { - Some(index) => *index, - None => { - let current_index = StructDefInstantiationIndex( - self.struct_instance_for_offset.len() as TableIndex, - ); - self.struct_instantiations - .insert(instantiant.clone(), current_index); - self.struct_instance_for_offset.push(instantiant); - current_index - }, - } - } - - /// If the `instantiant` is not in the `function_instantiations` table, this adds the - /// instantiant to the `func_instance_for_offset` for table, and adds the index to the - /// reverse lookup table. - /// Returns the SignatureIndex for the `instantiant`. - pub fn add_function_instantiation( - &mut self, - instantiant: FunctionInstantiation, - ) -> FunctionInstantiationIndex { - match self.function_instantiations.get(&instantiant) { - Some(index) => *index, - None => { - let current_index = - FunctionInstantiationIndex(self.func_instance_for_offset.len() as TableIndex); - self.function_instantiations - .insert(instantiant.clone(), current_index); - self.func_instance_for_offset.push(instantiant); - current_index - }, - } - } - - /// If the `instantiant` is not in the `field_instantiations` table, this adds the - /// instantiant to the `field_instance_for_offset` for table, and adds the index to the - /// reverse lookup table. - /// Returns the SignatureIndex for the `instantiant`. - pub fn add_field_instantiation( - &mut self, - instantiant: FieldInstantiation, - ) -> FieldInstantiationIndex { - match self.field_instantiations.get(&instantiant) { - Some(index) => *index, - None => { - let current_index = - FieldInstantiationIndex(self.field_instance_for_offset.len() as TableIndex); - self.field_instantiations - .insert(instantiant.clone(), current_index); - self.field_instance_for_offset.push(instantiant); - current_index - }, - } - } - - /// Returns the type instantiation at `index`. Errors if the instantiation does not exist. - pub fn instantiantiation_at(&self, index: SignatureIndex) -> &Vec { - match self.sig_instance_for_offset.get(index.0 as usize) { - Some(vec) => vec, - None => { - panic!("Unable to get instantiation at offset: {:#?}", index); - }, - } - } - - /// Returns the struct instantiation at `index`. Errors if the instantiation does not exist. - pub fn struct_instantiantiation_at( - &self, - index: StructDefInstantiationIndex, - ) -> &StructDefInstantiation { - match self.struct_instance_for_offset.get(index.0 as usize) { - Some(struct_inst) => struct_inst, - None => { - panic!("Unable to get instantiation at offset: {:#?}", index); - }, - } - } - - /// Returns the struct instantiation at `index`. Errors if the instantiation does not exist. - pub fn function_instantiantiation_at( - &self, - index: FunctionInstantiationIndex, - ) -> &FunctionInstantiation { - match self.func_instance_for_offset.get(index.0 as usize) { - Some(func_inst) => func_inst, - None => { - panic!("Unable to get instantiation at offset: {:#?}", index); - }, - } - } - - /// Returns the struct instantiation at `index`. Errors if the instantiation does not exist. - pub fn field_instantiantiation_at( - &self, - index: FieldInstantiationIndex, - ) -> &FieldInstantiation { - match self.field_instance_for_offset.get(index.0 as usize) { - Some(field_inst) => field_inst, - None => { - panic!("Unable to get instantiation at offset: {:#?}", index); - }, - } - } - - /// Consumes self, and adds the instantiations that have been built up to the underlying - /// module, and returns the resultant compiled module. - pub fn instantiate(self) -> CompiledModule { - let mut module = self.module; - module.signatures = self - .sig_instance_for_offset - .into_iter() - .map(Signature) - .collect(); - module.struct_def_instantiations = self.struct_instance_for_offset; - module.function_instantiations = self.func_instance_for_offset; - module.field_instantiations = self.field_instance_for_offset; - module - } -} - -/// An AbstractState represents an abstract view of the execution of the -/// Move VM. Rather than considering values of items on the stack or in -/// the locals, we only consider their type, represented by a `AbstractValue` -/// and their availibility, represented by the `BorrowState`. -#[derive(Debug, Clone)] -pub struct AbstractState { - /// A Vector of `AbstractValue`s representing the VM value stack - stack: Vec, - - /// A vector of type kinds for any generic function type parameters of the function that we are - /// in. - pub instantiation: Vec, - - /// A HashMap mapping local indicies to `AbstractValue`s and `BorrowState`s - locals: HashMap, - - /// Temporary location for storing the results of instruction effects for - /// access by subsequent instructions' effects - register: Option, - - /// The module state - pub module: InstantiableModule, - - /// The global resources acquired by the function corresponding to this abstract state - pub acquires_global_resources: Vec, - - /// This flag is set when applying an instruction that should result in an error - /// in the VM runtime. - aborted: bool, - - /// This flag controls whether or not control flow operators are allowed to be applied to the - /// abstract state. - control_flow_allowed: bool, - - /// This graph stores borrow information needed to ensure that bytecode instructions - /// are memory safe - #[allow(dead_code)] - borrow_graph: BorrowGraph, - - pub call_graph: CallGraph, -} - -impl AbstractState { - /// Create a new AbstractState with empty stack, locals, and register - pub fn new() -> AbstractState { - let compiled_module = empty_module(); - AbstractState { - stack: Vec::new(), - instantiation: Vec::new(), - locals: HashMap::new(), - register: None, - module: InstantiableModule::new(compiled_module), - acquires_global_resources: Vec::new(), - aborted: false, - control_flow_allowed: false, - borrow_graph: BorrowGraph::new(0), - call_graph: CallGraph::new(0), - } - } - - /// Create a new AbstractState given a list of `SignatureTokens` that will be - /// the (available) locals that the state will have, as well as the module state - pub fn from_locals( - module: CompiledModule, - locals: HashMap, - instantiation: Vec, - acquires_global_resources: Vec, - call_graph: CallGraph, - ) -> AbstractState { - let locals_len = locals.len(); - let module = InstantiableModule::new(module); - AbstractState { - stack: Vec::new(), - instantiation, - locals, - module, - register: None, - acquires_global_resources, - aborted: false, - control_flow_allowed: false, - borrow_graph: BorrowGraph::new(locals_len as u8), - call_graph, - } - } - - /// Get the register value - pub fn register_copy(&self) -> Option { - self.register.clone() - } - - /// Get the register value and set it to `None` - pub fn register_move(&mut self) -> Option { - let value = self.register.clone(); - self.register = None; - value - } - - /// Set the register value and set it to `None` - pub fn register_set(&mut self, value: AbstractValue) { - self.register = Some(value); - } - - /// Add a `AbstractValue` to the stack - pub fn stack_push(&mut self, item: AbstractValue) { - // Programs that are large enough to exceed this bound - // will not be generated - debug_assert!(self.stack.len() < usize::max_value()); - self.stack.push(item); - } - - /// Add a `AbstractValue` to the stack from the register - /// If the register is `None` return a `VMError` - pub fn stack_push_register(&mut self) -> Result<(), VMError> { - if let Some(abstract_value) = self.register_move() { - // Programs that are large enough to exceed this bound - // will not be generated - debug_assert!(self.stack.len() < usize::max_value()); - self.stack.push(abstract_value); - Ok(()) - } else { - Err(VMError::new("Error: No value in register".to_string())) - } - } - - /// Remove an `AbstractValue` from the stack if it exists to the register - /// If it does not exist return a `VMError`. - pub fn stack_pop(&mut self) -> Result<(), VMError> { - if self.stack.is_empty() { - Err(VMError::new("Pop attempted on empty stack".to_string())) - } else { - self.register = self.stack.pop(); - Ok(()) - } - } - - /// Get the `AbstractValue` at index `index` on the stack if it exists. - /// Index 0 is the top of the stack. - pub fn stack_peek(&self, index: usize) -> Option { - if index < self.stack.len() { - Some(self.stack[self.stack.len() - 1 - index].clone()) - } else { - None - } - } - - /// Get the length of the stack. - pub fn stack_len(&self) -> usize { - self.stack.len() - } - - /// Check if the local at index `i` exists - pub fn local_exists(&self, i: usize) -> bool { - self.locals.contains_key(&i) - } - - /// Get the local at index `i` if it exists - pub fn local_get(&self, i: usize) -> Option<&(AbstractValue, BorrowState)> { - self.locals.get(&i) - } - - /// Place the local at index `i` if it exists into the register - /// If it does not exist return a `VMError`. - pub fn local_take(&mut self, i: usize) -> Result<(), VMError> { - if let Some((abstract_value, _)) = self.locals.get(&i) { - self.register = Some(abstract_value.clone()); - Ok(()) - } else { - Err(VMError::new(format!("Local does not exist at index {}", i))) - } - } - - /// Place a reference to the local at index `i` if it exists into the register - /// If it does not exist return a `VMError`. - pub fn local_take_borrow(&mut self, i: usize, mutability: Mutability) -> Result<(), VMError> { - if let Some((abstract_value, _)) = self.locals.get(&i) { - let ref_token = match mutability { - Mutability::Mutable => { - SignatureToken::MutableReference(Box::new(abstract_value.token.clone())) - }, - Mutability::Immutable => { - SignatureToken::Reference(Box::new(abstract_value.token.clone())) - }, - Mutability::Either => { - return Err(VMError::new("Mutability cannot be Either".to_string())) - }, - }; - self.register = Some(AbstractValue::new_reference( - ref_token, - abstract_value.abilities, - )); - Ok(()) - } else { - Err(VMError::new(format!("Local does not exist at index {}", i))) - } - } - - /// Set the availability of the local at index `i` - /// If it does not exist return a `VMError`. - pub fn local_set(&mut self, i: usize, availability: BorrowState) -> Result<(), VMError> { - if let Some((abstract_value, _)) = self.locals.clone().get(&i) { - self.locals - .insert(i, (abstract_value.clone(), availability)); - Ok(()) - } else { - Err(VMError::new(format!("Local does not exist at index {}", i))) - } - } - - /// Check whether a local is in a particular `BorrowState` - /// If the local does not exist return a `VMError`. - pub fn local_availability_is( - &self, - i: usize, - availability: BorrowState, - ) -> Result { - if let Some((_, availability1)) = self.locals.get(&i) { - Ok(availability == *availability1) - } else { - Err(VMError::new(format!("Local does not exist at index {}", i))) - } - } - - /// Check whether a local has a particular `Ability` - /// If the local does not exist return a `VMError`. - pub fn local_has_ability(&self, i: usize, ability: Ability) -> Result { - if let Some((abstract_value, _)) = self.locals.get(&i) { - Ok(abstract_value.abilities.has_ability(ability)) - } else { - Err(VMError::new(format!("Local does not exist at index {}", i))) - } - } - - /// Insert a local at index `i` as `Available` - pub fn local_insert( - &mut self, - i: usize, - abstract_value: AbstractValue, - availability: BorrowState, - ) { - self.locals.insert(i, (abstract_value, availability)); - } - - /// Insert a local at index `i` as `Available` from the register - /// If the register value is `None` return a `VMError`. - pub fn local_place(&mut self, i: usize) -> Result<(), VMError> { - if let Some(abstract_value) = self.register_move() { - self.locals - .insert(i, (abstract_value, BorrowState::Available)); - Ok(()) - } else { - Err(VMError::new( - "Could not insert local, register is empty".to_string(), - )) - } - } - - /// Get all of the locals - pub fn get_locals(&self) -> &HashMap { - &self.locals - } - - /// Set the abstract state to be `aborted` when a precondition of an instruction - /// fails. (This will happen if `NEGATE_PRECONDITIONs` is true). - pub fn abort(&mut self) { - self.aborted = true; - } - - /// Whether the state is aborted - pub fn has_aborted(&self) -> bool { - self.aborted - } - - /// Set the abstract state to allow generation of control flow operations. - pub fn allow_control_flow(&mut self) { - self.control_flow_allowed = true; - } - - /// Predicate determining if control flow instructions can be generated. - pub fn is_control_flow_allowed(&self) -> bool { - self.control_flow_allowed - } - - /// The final state is one where the stack is empty - pub fn is_final(&self) -> bool { - self.stack.is_empty() - } -} - -impl fmt::Display for AbstractState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Stack: {:?} | Locals: {:?} | Instantiation: {:?}", - self.stack, self.locals, self.instantiation - ) - } -} - -impl Default for AbstractState { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Display for AbstractValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "({:?}: {:?})", self.token, self.abilities) - } -} diff --git a/third_party/move/testing-infra/test-generation/src/borrow_graph.rs b/third_party/move/testing-infra/test-generation/src/borrow_graph.rs deleted file mode 100644 index 9e9f6a517efef0..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/borrow_graph.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::abstract_state::Mutability; -use std::collections::HashMap; - -/// Each partition is associated with a (unique) ID -type PartitionID = u16; - -/// A nonce represents a runtime reference. It has a unique identifier and a mutability -type Nonce = (u16, Mutability); - -/// A set of nonces -type NonceSet = Vec; - -/// A path representing field accesses of a struct -type Path = Vec; - -/// An edge in the graph. It consists of two partition IDs, a `Path` which -/// may be empty, and an `EdgeType` -type Edge = (PartitionID, PartitionID, Path, EdgeType); - -/// The `EdgeType` is either weak or strong. A weak edge represents imprecise information -/// on the path along which the borrow takes place. A strong edge is precise. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum EdgeType { - Weak, - Strong, -} - -/// The `BorrowGraph` stores information sufficient to determine whether the instruction -/// of a bytecode instruction that interacts with references is memory safe. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowGraph { - /// All of the partitions that make up the graph - partitions: Vec, - - /// A mapping from partitions to the associated with them - partition_map: HashMap, - - /// The edges of the graph - edges: Vec, - - /// A counter for determining what the next partition ID should be - partition_counter: u16, -} - -impl BorrowGraph { - /// Construct a new `BorrowGraph` given the number of locals it has - pub fn new(num_locals: u8) -> BorrowGraph { - BorrowGraph { - partitions: Vec::with_capacity(num_locals as usize), - partition_map: HashMap::new(), - edges: Vec::new(), - partition_counter: u16::from(num_locals), - } - } - - /// Add a new partition to the graph containing nonce `n` - /// This operation may fail with an error a fresh partition ID - /// cannot be chosen. - pub fn fresh_partition(&mut self, n: Nonce) -> Result<(), String> { - if self.partition_counter.checked_add(1).is_some() { - if self.partition_map.contains_key(&self.partition_counter) { - return Err("Partition map already contains ID".to_string()); - } - self.partition_map.insert(self.partition_counter, vec![n]); - // Implication of `checked_add` - debug_assert!(self.partitions.len() < usize::max_value()); - self.partitions.push(self.partition_counter); - Ok(()) - } else { - Err("Partition map is full".to_string()) - } - } - - /// Determine whether a partition is mutable, immutable, or either. - /// This operation may fail with an error if the given partition does - /// not exist in the graph. - pub fn partition_mutability(&self, partition_id: PartitionID) -> Result { - if let Some(nonce_set) = self.partition_map.get(&partition_id) { - if nonce_set - .iter() - .all(|(_, mutability)| *mutability == Mutability::Mutable) - { - Ok(Mutability::Mutable) - } else if nonce_set - .iter() - .all(|(_, mutability)| *mutability == Mutability::Immutable) - { - Ok(Mutability::Immutable) - } else { - Ok(Mutability::Either) - } - } else { - Err("Partition map does not contain given partition ID".to_string()) - } - } - - /// Determine whether the given partition is freezable. This operation may fail - /// with an error if the given partition ID is not in the graph. - pub fn partition_freezable(&self, partition_id: PartitionID) -> Result { - let mut freezable = true; - if self.partition_map.contains_key(&partition_id) { - for (p1, p2, _, _) in self.edges.iter() { - if *p1 == partition_id && self.partition_mutability(*p2)? == Mutability::Mutable { - freezable = false; - } - } - Ok(freezable) - } else { - Err("Partition map does not contain given partition ID".to_string()) - } - } - - /// Determine whether the `path_1` is a prefix of `path_2` - fn path_is_prefix(&self, path_1: Path, path_2: Path) -> bool { - let mut prefix = true; - for (i, field) in path_1.iter().enumerate() { - if *field != path_2[i] { - prefix = false; - } - } - prefix - } - - /// Determine whether two edges are consistent; i.e. whether the path of the - /// first edge is a prefix of the second or vice versa. - pub fn edges_consistent(&self, edge_1: Edge, edge_2: Edge) -> bool { - let path_1 = edge_1.2; - let path_2 = edge_2.2; - self.path_is_prefix(path_1.clone(), path_2.clone()) || self.path_is_prefix(path_2, path_1) - } -} diff --git a/third_party/move/testing-infra/test-generation/src/bytecode_generator.rs b/third_party/move/testing-infra/test-generation/src/bytecode_generator.rs deleted file mode 100644 index e7c3d8137d257a..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/bytecode_generator.rs +++ /dev/null @@ -1,999 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -#![allow(clippy::manual_try_fold)] - -use crate::{ - abstract_state::{AbstractState, BorrowState, CallGraph, InstantiableModule}, - config::{ - CALL_STACK_LIMIT, INHABITATION_INSTRUCTION_LIMIT, MAX_CFG_BLOCKS, MUTATION_TOLERANCE, - NEGATE_PRECONDITIONS, NEGATION_PROBABILITY, VALUE_STACK_LIMIT, - }, - control_flow_graph::CFG, - substitute, summaries, -}; -use move_binary_format::{ - access::ModuleAccess, - file_format::{ - Bytecode, CodeOffset, CompiledModule, ConstantPoolIndex, FieldHandleIndex, - FieldInstantiationIndex, FunctionHandle, FunctionHandleIndex, FunctionInstantiation, - FunctionInstantiationIndex, LocalIndex, SignatureToken, StructDefInstantiation, - StructDefInstantiationIndex, StructDefinitionIndex, StructFieldInformation, TableIndex, - }, -}; -use move_core_types::u256::U256; -use rand::{rngs::StdRng, Rng}; -use tracing::{debug, error, warn}; - -/// This type represents bytecode instructions that take a `LocalIndex` -type LocalIndexToBytecode = fn(LocalIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `u16` -type CodeOffsetToBytecode = fn(CodeOffset) -> Bytecode; - -/// This type represents bytecode instructions that take a `u8` -type U8ToBytecode = fn(u8) -> Bytecode; - -/// This type represents bytecode instructions that take a `u16` -type U16ToBytecode = fn(u16) -> Bytecode; - -/// This type represents bytecode instructions that take a `u32` -type U32ToBytecode = fn(u32) -> Bytecode; - -/// This type represents bytecode instructions that take a `u64` -type U64ToBytecode = fn(u64) -> Bytecode; - -/// This type represents bytecode instructions that take a `u128` -type U128ToBytecode = fn(u128) -> Bytecode; - -/// This type represents bytecode instructions that take a `u256` -type U256ToBytecode = fn(U256) -> Bytecode; - -/// This type represents bytecode instructions that take a `AddressPoolIndex` -type ConstantPoolIndexToBytecode = fn(ConstantPoolIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `StructDefinitionIndex` -type StructIndexToBytecode = fn(StructDefinitionIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `FieldHandleIndex` -type FieldHandleIndexToBytecode = fn(FieldHandleIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `FunctionHandleIndex` -type FunctionIndexToBytecode = fn(FunctionHandleIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `StructDefInstantiationIndex` -type StructInstIndexToBytecode = fn(StructDefInstantiationIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `FieldInstantiationIndex` -type FieldInstIndexToBytecode = fn(FieldInstantiationIndex) -> Bytecode; - -/// This type represents bytecode instructions that take a `FunctionInstantiationIndex` -type FunctionInstIndexToBytecode = fn(FunctionInstantiationIndex) -> Bytecode; - -/// There are six types of bytecode instructions -#[derive(Debug, Clone)] -enum BytecodeType { - /// Instructions that do not take an argument - NoArg(Bytecode), - - /// Instructions that take a `LocalIndex` - LocalIndex(LocalIndexToBytecode), - - /// Instructions that take a `CodeOffset` - CodeOffset(CodeOffsetToBytecode), - - /// Instructions that take a `u8` - U8(U8ToBytecode), - - /// Instructions that take a `u16` - U16(U16ToBytecode), - - /// Instructions that take a `u32` - U32(U32ToBytecode), - - /// Instructions that take a `u64` - U64(U64ToBytecode), - - /// Instructions that take a `u128` - U128(U128ToBytecode), - - /// Instructions that take a `u256` - U256(U256ToBytecode), - - /// Instructions that take an `ConstantPoolIndex` - ConstantPoolIndex(ConstantPoolIndexToBytecode), - - /// Instructions that take a `StructDefinitionIndex` - StructIndex(StructIndexToBytecode), - - /// Instructions that take a `FieldHandleIndex` - FieldHandleIndex(FieldHandleIndexToBytecode), - - /// Instructions that take a `FunctionHandleIndex` - FunctionIndex(FunctionIndexToBytecode), - - /// Instructions that take a `StructInstantiationIndex` - StructInstantiationIndex(StructInstIndexToBytecode), - - /// Instructions that take a `FieldInstantiationIndex` - FieldInstantiationIndex(FieldInstIndexToBytecode), - - /// Instructions that take a `FunctionInstantiationIndex` - FunctionInstantiationIndex(FunctionInstIndexToBytecode), -} - -/// Abstraction for change to the stack size -#[derive(Debug, Copy, Clone, PartialEq)] -enum StackEffect { - /// Represents an increase in stack size - Add, - - /// Represents a decrease in stack size - Sub, - - /// Represents no change in stack size - Nop, -} - -/// Context containing information about a function -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct FunctionGenerationContext { - pub function_handle_index: FunctionHandleIndex, - pub starting_call_height: usize, - pub locals_len: usize, - pub bytecode_len: u64, -} - -impl FunctionGenerationContext { - pub fn new( - function_handle_index: FunctionHandleIndex, - starting_call_height: usize, - locals_len: usize, - bytecode_len: u64, - ) -> Self { - Self { - function_handle_index, - starting_call_height, - locals_len, - bytecode_len, - } - } - - pub fn incr_instruction_count(&mut self) -> Option<()> { - self.bytecode_len += 1; - if self.bytecode_len >= (u16::max_value() - 1) as u64 { - return None; - } - Some(()) - } -} - -/// Generates a sequence of bytecode instructions. -/// This generator has: -/// - `instructions`: A list of bytecode instructions to use for generation -/// - `rng`: A random number generator for uniform random choice of next instruction -#[derive(Debug)] -pub struct BytecodeGenerator<'a> { - instructions: Vec<(StackEffect, BytecodeType)>, - rng: &'a mut StdRng, -} - -impl<'a> BytecodeGenerator<'a> { - /// The `BytecodeGenerator` is instantiated with a seed to use with - /// its random number generator. - pub fn new(rng: &'a mut StdRng) -> Self { - let instructions: Vec<(StackEffect, BytecodeType)> = vec![ - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Pop)), - (StackEffect::Add, BytecodeType::U8(Bytecode::LdU8)), - (StackEffect::Add, BytecodeType::U16(Bytecode::LdU16)), - (StackEffect::Add, BytecodeType::U32(Bytecode::LdU32)), - (StackEffect::Add, BytecodeType::U64(Bytecode::LdU64)), - (StackEffect::Add, BytecodeType::U128(Bytecode::LdU128)), - (StackEffect::Add, BytecodeType::U256(Bytecode::LdU256)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU8)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU16)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU32)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU64)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU128)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::CastU256)), - ( - StackEffect::Add, - BytecodeType::ConstantPoolIndex(Bytecode::LdConst), - ), - (StackEffect::Add, BytecodeType::NoArg(Bytecode::LdTrue)), - (StackEffect::Add, BytecodeType::NoArg(Bytecode::LdFalse)), - ( - StackEffect::Add, - BytecodeType::LocalIndex(Bytecode::CopyLoc), - ), - ( - StackEffect::Add, - BytecodeType::LocalIndex(Bytecode::MoveLoc), - ), - (StackEffect::Sub, BytecodeType::LocalIndex(Bytecode::StLoc)), - ( - StackEffect::Add, - BytecodeType::LocalIndex(Bytecode::MutBorrowLoc), - ), - ( - StackEffect::Add, - BytecodeType::LocalIndex(Bytecode::ImmBorrowLoc), - ), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::ReadRef)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::WriteRef)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::FreezeRef)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Add)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Sub)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Mul)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Div)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Mod)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::BitAnd)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::BitOr)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Xor)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Or)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::And)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::Not)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Eq)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Neq)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Lt)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Gt)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Le)), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Ge)), - (StackEffect::Nop, BytecodeType::StructIndex(Bytecode::Pack)), - ( - StackEffect::Nop, - BytecodeType::StructInstantiationIndex(Bytecode::PackGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::StructIndex(Bytecode::Unpack), - ), - ( - StackEffect::Nop, - BytecodeType::StructInstantiationIndex(Bytecode::UnpackGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::StructIndex(Bytecode::Exists), - ), - ( - StackEffect::Nop, - BytecodeType::StructInstantiationIndex(Bytecode::ExistsGeneric), - ), - ( - StackEffect::Add, - BytecodeType::StructIndex(Bytecode::MoveFrom), - ), - ( - StackEffect::Add, - BytecodeType::StructInstantiationIndex(Bytecode::MoveFromGeneric), - ), - ( - StackEffect::Sub, - BytecodeType::StructIndex(Bytecode::MoveTo), - ), - ( - StackEffect::Sub, - BytecodeType::StructInstantiationIndex(Bytecode::MoveToGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::StructIndex(Bytecode::MutBorrowGlobal), - ), - ( - StackEffect::Nop, - BytecodeType::StructInstantiationIndex(Bytecode::MutBorrowGlobalGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::StructIndex(Bytecode::ImmBorrowGlobal), - ), - ( - StackEffect::Nop, - BytecodeType::StructInstantiationIndex(Bytecode::ImmBorrowGlobalGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::FieldHandleIndex(Bytecode::MutBorrowField), - ), - ( - StackEffect::Nop, - BytecodeType::FieldInstantiationIndex(Bytecode::MutBorrowFieldGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::FieldHandleIndex(Bytecode::ImmBorrowField), - ), - ( - StackEffect::Nop, - BytecodeType::FieldInstantiationIndex(Bytecode::ImmBorrowFieldGeneric), - ), - ( - StackEffect::Nop, - BytecodeType::FunctionIndex(Bytecode::Call), - ), - ( - StackEffect::Nop, - BytecodeType::FunctionInstantiationIndex(Bytecode::CallGeneric), - ), - (StackEffect::Nop, BytecodeType::CodeOffset(Bytecode::Branch)), - (StackEffect::Sub, BytecodeType::CodeOffset(Bytecode::BrTrue)), - ( - StackEffect::Sub, - BytecodeType::CodeOffset(Bytecode::BrFalse), - ), - (StackEffect::Sub, BytecodeType::NoArg(Bytecode::Abort)), - (StackEffect::Nop, BytecodeType::NoArg(Bytecode::Ret)), - ]; - Self { instructions, rng } - } - - fn index_or_none(table: &[T], rng: &mut StdRng) -> Option { - if table.is_empty() { - None - } else { - Some(rng.gen_range(0..table.len()) as TableIndex) - } - } - - // Soft cutoff: We starting making it less likely for the stack size to be increased once it - // becomes greater than VALUE_STACK_LIMIT - 24. Once we reach the stack limit then we have a - // hard cutoff. - fn value_backpressure(state: &AbstractState, probability: f32) -> f32 { - let len = state.stack_len(); - - if len <= VALUE_STACK_LIMIT - 24 { - return probability; - } - - if len > VALUE_STACK_LIMIT - 24 { - return probability * 0.5; - } - - 0.0 - } - - // Tight cutoff: calls can be generated as long as it's less than the max call stack height. If - // the call would cause the call stack to overflow then it can't be generated. - fn call_stack_backpressure( - state: &AbstractState, - fn_context: &FunctionGenerationContext, - call: FunctionHandleIndex, - ) -> Option { - if let Some(call_size) = state - .call_graph - .call_depth(fn_context.function_handle_index, call) - { - if call_size + fn_context.starting_call_height <= CALL_STACK_LIMIT { - return Some(call); - } - } - None - } - - /// Given an `AbstractState`, `state`, and a the number of locals the function has, - /// this function returns a list of instructions whose preconditions are satisfied for - /// the state. - fn candidate_instructions( - &mut self, - fn_context: &FunctionGenerationContext, - state: AbstractState, - module: CompiledModule, - ) -> Vec<(StackEffect, Bytecode)> { - let mut matches: Vec<(StackEffect, Bytecode)> = Vec::new(); - let instructions = &self.instructions; - for (stack_effect, instruction) in instructions.iter() { - let instruction: Option = match instruction { - BytecodeType::NoArg(instruction) => Some(instruction.clone()), - BytecodeType::LocalIndex(instruction) => { - // Generate a random index into the locals - if fn_context.locals_len > 0 { - Some(instruction( - self.rng.gen_range(0..fn_context.locals_len) as LocalIndex - )) - } else { - None - } - }, - BytecodeType::CodeOffset(instruction) => { - // Set 0 as the offset. This will be set correctly during serialization - Some(instruction(0)) - }, - BytecodeType::U8(instruction) => { - // Generate a random u8 constant to load - Some(instruction(self.rng.gen_range(0..u8::max_value()))) - }, - BytecodeType::U16(instruction) => { - // Generate a random u16 constant to load - Some(instruction(self.rng.gen_range(0..u16::max_value()))) - }, - BytecodeType::U32(instruction) => { - // Generate a random u32 constant to load - Some(instruction(self.rng.gen_range(0..u32::max_value()))) - }, - BytecodeType::U64(instruction) => { - // Generate a random u64 constant to load - Some(instruction(self.rng.gen_range(0..u64::max_value()))) - }, - BytecodeType::U128(instruction) => { - // Generate a random u128 constant to load - Some(instruction(self.rng.gen_range(0..u128::max_value()))) - }, - BytecodeType::U256(instruction) => { - // Generate a random u256 constant to load - Some(instruction( - self.rng.gen_range(U256::zero()..U256::max_value()), - )) - }, - BytecodeType::ConstantPoolIndex(instruction) => { - // Select a random address from the module's address pool - Self::index_or_none(&module.constant_pool, self.rng) - .map(|x| instruction(ConstantPoolIndex::new(x))) - }, - BytecodeType::StructIndex(instruction) => { - // Select a random struct definition and local signature - Self::index_or_none(&module.struct_defs, self.rng) - .map(|x| instruction(StructDefinitionIndex::new(x))) - }, - BytecodeType::FieldHandleIndex(instruction) => { - // Select a field definition from the module's field definitions - Self::index_or_none(&module.field_handles, self.rng) - .map(|x| instruction(FieldHandleIndex::new(x))) - }, - BytecodeType::FunctionIndex(instruction) => { - // Select a random function handle and local signature - let callable_fns = &state.call_graph.can_call(fn_context.function_handle_index); - Self::index_or_none(callable_fns, self.rng) - .and_then(|handle_idx| { - Self::call_stack_backpressure( - &state, - fn_context, - callable_fns[handle_idx as usize], - ) - }) - .map(instruction) - }, - BytecodeType::StructInstantiationIndex(instruction) => { - // Select a field definition from the module's field definitions - Self::index_or_none(&module.struct_def_instantiations, self.rng) - .map(|x| instruction(StructDefInstantiationIndex::new(x))) - }, - BytecodeType::FunctionInstantiationIndex(instruction) => { - // Select a field definition from the module's field definitions - Self::index_or_none(&module.function_instantiations, self.rng) - .map(|x| instruction(FunctionInstantiationIndex::new(x))) - }, - BytecodeType::FieldInstantiationIndex(instruction) => { - // Select a field definition from the module's field definitions - Self::index_or_none(&module.field_instantiations, self.rng) - .map(|x| instruction(FieldInstantiationIndex::new(x))) - }, - }; - if let Some(instruction) = instruction { - let summary = summaries::instruction_summary(instruction.clone(), false); - let unsatisfied_preconditions = summary - .preconditions - .iter() - .filter(|precondition| !precondition(&state)) - .count(); - if (NEGATE_PRECONDITIONS - && !summary.preconditions.is_empty() - && unsatisfied_preconditions - > self.rng.gen_range(0..summary.preconditions.len()) - && self.rng.gen_range(0..101) > 100 - (NEGATION_PROBABILITY * 100.0) as u8) - || unsatisfied_preconditions == 0 - { - // The size of matches cannot be greater than the number of bytecode instructions - debug_assert!(matches.len() < usize::max_value()); - matches.push((*stack_effect, instruction)); - } - } - } - matches - } - - /// Select an instruction from the list of candidates based on the current state's - /// stack size and the expected number of function return parameters. - fn select_candidate( - &mut self, - return_len: usize, - state: &AbstractState, - candidates: &[(StackEffect, Bytecode)], - ) -> Result { - debug!("Candidates: {:?}", candidates); - let stack_len = state.stack_len(); - let prob_add = if stack_len > return_len { - MUTATION_TOLERANCE / (stack_len as f32) - } else { - 1.0 - }; - let prob_add = Self::value_backpressure(state, prob_add); - debug!("Pr[add] = {:?}", prob_add); - let next_instruction_index; - if self.rng.gen_range(0.0..1.0) <= prob_add { - let add_candidates: Vec = candidates - .iter() - .filter(|(stack_effect, _)| { - *stack_effect == StackEffect::Add || *stack_effect == StackEffect::Nop - }) - .map(|(_, candidate)| candidate) - .cloned() - .collect(); - // Add candidates should not be empty unless the list of bytecode instructions is - // changed - if add_candidates.is_empty() { - return Err("Could not find valid add candidate".to_string()); - } - next_instruction_index = self.rng.gen_range(0..add_candidates.len()); - Ok(add_candidates[next_instruction_index].clone()) - } else { - let sub_candidates: Vec = candidates - .iter() - .filter(|(stack_effect, _)| { - *stack_effect == StackEffect::Sub || *stack_effect == StackEffect::Nop - }) - .map(|(_, candidate)| candidate) - .cloned() - .collect(); - // Sub candidates should not be empty unless the list of bytecode instructions is - // changed - if sub_candidates.is_empty() { - return Err("Could not find sub valid candidate".to_string()); - } - next_instruction_index = self.rng.gen_range(0..sub_candidates.len()); - Ok(sub_candidates[next_instruction_index].clone()) - } - } - - /// Transition an abstract state, `state` to the next state by applying all of the effects - /// of a particular bytecode instruction, `instruction`. - fn abstract_step( - &self, - mut state: AbstractState, - instruction: Bytecode, - exact: bool, - ) -> (AbstractState, Bytecode) { - let summary = summaries::instruction_summary(instruction.clone(), exact); - let should_error = summary - .preconditions - .iter() - .any(|precondition| !precondition(&state)); - if should_error { - debug!("Reached abort state"); - state.abort(); - } - let apply_effects = |state, effects: Vec>| { - effects.iter().fold(state, |acc, effect| { - effect(&acc).unwrap_or_else(|err| { - if NEGATE_PRECONDITIONS { - // Ignore the effect - acc - } else { - unreachable!("Error applying instruction effect: {}", err); - } - }) - }) - }; - match summary.effects { - summaries::Effects::TyParams(instantiation, effect, instantiation_application) => { - let (struct_idx, instantiation) = instantiation(&state); - let index = state.module.add_instantiation(instantiation); - let struct_inst = StructDefInstantiation { - def: struct_idx, - type_parameters: index, - }; - let str_inst_idx = state.module.add_struct_instantiation(struct_inst); - let effects = effect(str_inst_idx); - let instruction = instantiation_application(str_inst_idx); - (apply_effects(state, effects), instruction) - }, - summaries::Effects::TyParamsCall(instantiation, effect, instantiation_application) => { - let (fh_idx, instantiation) = instantiation(&state); - let index = state.module.add_instantiation(instantiation); - let func_inst = FunctionInstantiation { - handle: fh_idx, - type_parameters: index, - }; - let func_inst_idx = state.module.add_function_instantiation(func_inst); - let effects = effect(func_inst_idx); - let instruction = instantiation_application(func_inst_idx); - (apply_effects(state, effects), instruction) - }, - summaries::Effects::NoTyParams(effects) => (apply_effects(state, effects), instruction), - } - } - - /// Transition an abstract state, `state` to the next state and add the instruction - /// to the bytecode sequence - pub fn apply_instruction( - &self, - fn_context: &mut FunctionGenerationContext, - mut state: AbstractState, - bytecode: &mut Vec, - instruction: Bytecode, - exact: bool, - ) -> Option { - // Bytecode will never be generated this large - debug_assert!(bytecode.len() < usize::max_value()); - debug!("**********************"); - debug!("State1: {}", state); - debug!("Next instr: {:?}", instruction); - let step = self.abstract_step(state, instruction, exact); - state = step.0; - let instruction = step.1; - debug!("Affected: {}", state); - debug!("Actual instr: {:?}", instruction); - if let Bytecode::Call(index) = instruction { - state - .call_graph - .add_call(fn_context.function_handle_index, index); - } - bytecode.push(instruction); - fn_context.incr_instruction_count()?; - debug!("**********************\n"); - Some(state) - } - - /// Given a valid starting state `abstract_state_in`, generate a valid sequence of - /// bytecode instructions such that `abstract_state_out` is reached. - pub fn generate_block( - &mut self, - fn_context: &mut FunctionGenerationContext, - abstract_state_in: AbstractState, - abstract_state_out: AbstractState, - module: &CompiledModule, - ) -> Option<(Vec, AbstractState)> { - debug!("Abstract state in: {}", abstract_state_in); - debug!("Abstract state out: {}", abstract_state_out); - let mut bytecode: Vec = Vec::new(); - let mut state = abstract_state_in.clone(); - // Generate block body - loop { - let candidates = self.candidate_instructions(fn_context, state.clone(), module.clone()); - if candidates.is_empty() { - warn!("No candidates found for state: [{:?}]", state); - break; - } - match self.select_candidate(0, &state, &candidates) { - Ok(next_instruction) => { - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - next_instruction, - false, - )?; - if state.is_final() { - break; - } else if state.has_aborted() { - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - Bytecode::LdU64(0), - true, - )?; - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - Bytecode::Abort, - true, - )?; - return Some((bytecode, state)); - } - }, - Err(err) => { - // Could not complete the bytecode sequence; reset to empty - error!("{}", err); - return Some((Vec::new(), abstract_state_in)); - }, - } - } - // Fix local availability - for (i, (abstract_value, target_availability)) in abstract_state_out.get_locals().iter() { - if let Some((_, current_availability)) = state.local_get(*i) { - if *target_availability == BorrowState::Available - && *current_availability == BorrowState::Unavailable - { - let next_instructions = - Self::inhabit_with_bytecode_seq(&mut state.module, &abstract_value.token); - debug!( - "local availability instructions: {:#?} for token {:#?}", - next_instructions, &abstract_value.token - ); - if next_instructions.len() >= INHABITATION_INSTRUCTION_LIMIT { - return None; - } - state = - next_instructions - .into_iter() - .fold(Some(state), |state, instruction| { - state.and_then(|state| { - self.apply_instruction( - fn_context, - state, - &mut bytecode, - instruction, - true, - ) - }) - })?; - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - Bytecode::StLoc(*i as u8), - true, - )?; - } else if *target_availability == BorrowState::Unavailable - && *current_availability == BorrowState::Available - { - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - Bytecode::MoveLoc(*i as u8), - true, - )?; - state = self.apply_instruction( - fn_context, - state, - &mut bytecode, - Bytecode::Pop, - true, - )?; - } - } else { - unreachable!("Target locals out contains new local"); - } - } - // Update the module to be the module that we've been building in our abstract state - Some((bytecode, state)) - } - - /// Generate the body of a function definition given a set of starting `locals` and a target - /// return `signature`. The sequence should contain at least `target_min` and at most - /// `target_max` instructions. - pub fn generate( - &mut self, - fn_context: &mut FunctionGenerationContext, - locals: &[SignatureToken], - fh: &FunctionHandle, - acquires_global_resources: &[StructDefinitionIndex], - module: &mut CompiledModule, - call_graph: &mut CallGraph, - ) -> Option> { - let number_of_blocks = self.rng.gen_range(1..=MAX_CFG_BLOCKS); - // The number of basic blocks must be at least one based on the - // generation range. - debug_assert!(number_of_blocks > 0); - let mut cfg = CFG::new( - self.rng, - locals, - &module.signatures[fh.parameters.0 as usize], - number_of_blocks, - ); - let cfg_copy = cfg.clone(); - for (block_id, block) in cfg.get_basic_blocks_mut().iter_mut() { - debug!( - "+++++++++++++++++ Starting new block: {} +++++++++++++++++", - block_id - ); - let state1 = AbstractState::from_locals( - module.clone(), - block.get_locals_in().clone(), - fh.type_parameters.clone(), - acquires_global_resources.to_vec(), - call_graph.clone(), - ); - let state2 = AbstractState::from_locals( - module.clone(), - block.get_locals_out().clone(), - fh.type_parameters.clone(), - acquires_global_resources.to_vec(), - call_graph.clone(), - ); - let (mut bytecode, mut state_f) = - self.generate_block(fn_context, state1, state2.clone(), module)?; - state_f.allow_control_flow(); - if !state_f.has_aborted() { - state_f = if cfg_copy.num_children(*block_id) == 2 { - // BrTrue, BrFalse: Add bool and branching instruction randomly - state_f = self.apply_instruction( - fn_context, - state_f, - &mut bytecode, - Bytecode::LdFalse, - true, - )?; - if self.rng.gen_bool(0.5) { - self.apply_instruction( - fn_context, - state_f, - &mut bytecode, - Bytecode::BrTrue(0), - true, - )? - } else { - self.apply_instruction( - fn_context, - state_f, - &mut bytecode, - Bytecode::BrFalse(0), - true, - )? - } - } else if cfg_copy.num_children(*block_id) == 1 { - // Branch: Add branch instruction - self.apply_instruction( - fn_context, - state_f, - &mut bytecode, - Bytecode::Branch(0), - true, - )? - } else if cfg_copy.num_children(*block_id) == 0 { - // Return: Add return types to last block - for token_type in module.signatures[fh.return_.0 as usize].0.iter() { - let next_instructions = - Self::inhabit_with_bytecode_seq(&mut state_f.module, token_type); - debug!( - "Return value instructions: {:#?} for token {:#?}", - next_instructions, &token_type - ); - state_f = next_instructions.into_iter().fold( - Some(state_f), - |state_f, instruction| { - state_f.and_then(|state_f| { - self.apply_instruction( - fn_context, - state_f, - &mut bytecode, - instruction, - true, - ) - }) - }, - )?; - } - self.apply_instruction(fn_context, state_f, &mut bytecode, Bytecode::Ret, true)? - } else { - state_f - }; - } - block.set_instructions(bytecode); - *module = state_f.module.instantiate(); - *call_graph = state_f.call_graph; - } - // The CFG will be non-empty if we set the number of basic blocks to generate - // to be non-zero - debug_assert!(number_of_blocks > 0 || cfg.get_basic_blocks().is_empty()); - Some(cfg.serialize()) - } - - pub fn generate_module(&mut self, mut module: CompiledModule) -> Option { - let mut fdefs = module.function_defs.clone(); - let mut call_graph = CallGraph::new(module.function_handles.len()); - for fdef in fdefs.iter_mut() { - if let Some(code) = &mut fdef.code { - let f_handle = &module.function_handles[fdef.function.0 as usize].clone(); - let locals_sigs = module.signatures[code.locals.0 as usize].0.clone(); - let mut fn_context = FunctionGenerationContext::new( - fdef.function, - call_graph.max_calling_depth(fdef.function), - locals_sigs.len(), - 0, - ); - code.code = self.generate( - &mut fn_context, - &locals_sigs, - f_handle, - &fdef.acquires_global_resources, - &mut module, - &mut call_graph, - )?; - } - } - module.function_defs = fdefs; - Some(module) - } - - /// Generate a sequence of instructions whose overall effect is to push a single value of type token - /// on the stack, specifically without consuming any values that existed on the stack prior to the - /// execution of the instruction sequence. - pub fn inhabit_with_bytecode_seq( - module: &mut InstantiableModule, - token: &SignatureToken, - ) -> Vec { - match token { - SignatureToken::Address => vec![Bytecode::LdConst(ConstantPoolIndex(0))], - SignatureToken::U64 => vec![Bytecode::LdU64(0)], - SignatureToken::U8 => vec![Bytecode::LdU8(0)], - SignatureToken::U128 => vec![Bytecode::LdU128(0)], - SignatureToken::U16 => vec![Bytecode::LdU16(0)], - SignatureToken::U32 => vec![Bytecode::LdU32(0)], - SignatureToken::U256 => vec![Bytecode::LdU256(U256::zero())], - SignatureToken::Bool => vec![Bytecode::LdFalse], - SignatureToken::Struct(handle_idx) => { - let struct_def_idx = module - .module - .struct_defs() - .iter() - .position(|struct_def| struct_def.struct_handle == *handle_idx) - .expect( - "struct def should exist for every struct handle in the test generator", - ); - let struct_def = module - .module - .struct_def_at(StructDefinitionIndex(struct_def_idx as TableIndex)); - let fields = match &struct_def.field_information { - StructFieldInformation::Native => panic!("Can't inhabit native structs"), - StructFieldInformation::DeclaredVariants(..) => { - // TODO(#13806): consider implementing for variants - panic!("Can't work with enum variants") - }, - StructFieldInformation::Declared(fields) => fields.clone(), - }; - let mut bytecodes: Vec = fields - .iter() - .flat_map(|field| { - let field_sig_tok = &field.signature.0; - Self::inhabit_with_bytecode_seq(module, field_sig_tok) - }) - .collect(); - bytecodes.push(Bytecode::Pack(StructDefinitionIndex( - struct_def_idx as TableIndex, - ))); - bytecodes - }, - SignatureToken::StructInstantiation(handle_idx, instantiation) => { - let struct_def_idx = module - .module - .struct_defs() - .iter() - .position(|struct_def| struct_def.struct_handle == *handle_idx) - .expect( - "struct def should exist for every struct handle in the test generator", - ); - let struct_def = module - .module - .struct_def_at(StructDefinitionIndex(struct_def_idx as TableIndex)); - let fields = match &struct_def.field_information { - StructFieldInformation::Native => panic!("Can't inhabit native structs"), - StructFieldInformation::DeclaredVariants(..) => { - // TODO(#13806): consider adding support for variants - panic!("Can't work with enum variants") - }, - StructFieldInformation::Declared(fields) => fields.clone(), - }; - let mut bytecodes: Vec = fields - .iter() - .flat_map(|field| { - let field_sig_tok = &field.signature.0; - let reified_field_sig_tok = substitute(field_sig_tok, instantiation); - Self::inhabit_with_bytecode_seq(module, &reified_field_sig_tok) - }) - .collect(); - let instantiation_index = module.add_instantiation(instantiation.clone()); - let struct_inst = StructDefInstantiation { - def: StructDefinitionIndex(struct_def_idx as TableIndex), - type_parameters: instantiation_index, - }; - let si_idx = module.add_struct_instantiation(struct_inst); - bytecodes.push(Bytecode::PackGeneric(StructDefInstantiationIndex( - si_idx.0 as TableIndex, - ))); - bytecodes - }, - SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::TypeParameter(_) => { - unimplemented!("Unsupported inhabitation. Type: {:#?}", token) - }, - } - } -} diff --git a/third_party/move/testing-infra/test-generation/src/config.rs b/third_party/move/testing-infra/test-generation/src/config.rs deleted file mode 100644 index c05df54c6dfc13..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/config.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use clap::Parser; -use module_generation::ModuleGeneratorOptions; - -/// This defines how tolerant the generator will be about deviating from -/// the starting stack height. -/// Default is `0.9` -pub const MUTATION_TOLERANCE: f32 = 0.9; - -/// This defines the maximum number of blocks that will be generated for -/// a function body's CFG. During generation, a random number of blocks from -/// 1 to this constant will be created. -/// Default is `10` -pub const MAX_CFG_BLOCKS: u16 = 10; - -/// Whether preconditions will be negated to generate invalid programs -/// in order to test error paths. -/// Default is `false` -pub const NEGATE_PRECONDITIONS: bool = false; - -/// The probability that preconditions will be negated for a pariticular -/// bytecode instruction. -/// Default is `0.1` -pub const NEGATION_PROBABILITY: f64 = 0.1; - -/// Whether generation of instructions that require borrow checking will -/// be allowed. (Note that if `NEGATE_PRECONDITIONS` is true then these -/// instructions can still come up). -/// Default is `false` -pub const ALLOW_MEMORY_UNSAFE: bool = false; - -/// Whether the generated programs should be run on the VM -/// Default is `true` -pub const RUN_ON_VM: bool = true; - -/// Whether generated modules will be executed even if they fail the -/// the bytecode verifier. -/// Default is `false` -pub const EXECUTE_UNVERIFIED_MODULE: bool = false; - -/// Whether gas will be metered when running generated programs. The default -/// is `true` to bound the execution time. -/// Default is `true` -pub const GAS_METERING: bool = true; - -/// Call stack height limit. This is defined in the VM, and is replicated here. This should track -/// that constant. -pub const CALL_STACK_LIMIT: usize = 1024; - -/// The value stack size limit. This is defined in the VM and is replicated here. This should -/// remain in sync with the constant for this defined in the VM. -pub const VALUE_STACK_LIMIT: usize = 1024; - -/// Certain randomly generated types can lead to extremely long instruction sequences. This can -/// lead to test generation taking quite a while in order to handle all of these. This parameter -/// bounds the maximum allowable instruction length for a type. If the instruction sequence is -/// larger then this, a new module and bytecode generation will be attempted. -pub const INHABITATION_INSTRUCTION_LIMIT: usize = 1000; - -/// The module generation settings that are used for generation module scaffolding for bytecode -/// generation. -pub fn module_generation_settings() -> ModuleGeneratorOptions { - ModuleGeneratorOptions { - min_table_size: 10, - // The more structs, and the larger the number of type parameters the more complex the - // functions and bytecode sequences generated. Be careful about setting these parameters too - // large -- this can lead to expontial increases in the size and number of struct - // instantiations that can be generated. - max_ty_params: 4, - max_functions: 6, - max_fields: 10, - max_structs: 6, - args_for_ty_params: true, - references_allowed: false, - // Test generation cannot currently cope with resources - add_resources: false, - ..Default::default() - } -} - -/// Command line arguments for the tool -#[derive(Debug, Parser)] -#[clap(author, version, about)] -pub struct Args { - /// The optional number of programs that will be generated. If not specified, program - /// generation will run infinitely. - #[clap(short = 'i', long = "iterations")] - pub num_iterations: Option, - - /// Path where a serialized module should be saved. - /// If `None`, then the module will just be printed out. - #[clap(short = 'o', long = "output")] - pub output_path: Option, - - /// The optional seed used for test generation. - #[clap(short = 's', long = "seed")] - pub seed: Option, - - /// The optional number of threads to use for test generation. - #[clap(short = 't', long = "threads")] - pub num_threads: Option, -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Args::command().debug_assert() -} diff --git a/third_party/move/testing-infra/test-generation/src/control_flow_graph.rs b/third_party/move/testing-infra/test-generation/src/control_flow_graph.rs deleted file mode 100644 index cdc8a9380b8283..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/control_flow_graph.rs +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::abstract_state::{AbstractValue, BorrowState}; -use move_binary_format::file_format::{AbilitySet, Bytecode, Signature, SignatureToken}; -use rand::{rngs::StdRng, Rng}; -use std::collections::{HashMap, VecDeque}; -use tracing::debug; - -/// This type holds basic block identifiers -type BlockIDSize = u16; - -/// This type represents the locals that a basic block has -type BlockLocals = HashMap; - -/// This represents a basic block in a control flow graph -#[derive(Debug, Default, Clone)] -pub struct BasicBlock { - /// The starting locals - locals_in: BlockLocals, - - /// The locals at the end of the block - locals_out: BlockLocals, - - /// The instructions that comprise the block - instructions: Vec, -} - -impl BasicBlock { - pub fn new() -> BasicBlock { - BasicBlock { - locals_in: HashMap::new(), - locals_out: HashMap::new(), - instructions: Vec::new(), - } - } - - /// Get the locals coming into the block - pub fn get_locals_in(&self) -> &BlockLocals { - &self.locals_in - } - - /// Get the locals going out of the block - pub fn get_locals_out(&self) -> &BlockLocals { - &self.locals_out - } - - /// Set the list of instructions that comprise the block - pub fn set_instructions(&mut self, instructions: Vec) { - self.instructions = instructions - } -} - -/// A control flow graph -#[derive(Debug, Clone)] -pub struct CFG { - /// The set of basic blocks that make up the graph, mapped to `BlockIDSize`'s used - /// as their identifiers - basic_blocks: HashMap, - - /// The directed edges of the graph represented by pairs of basic block identifiers - edges: Vec<(BlockIDSize, BlockIDSize)>, -} - -impl CFG { - /// Construct a control flow graph that contains empty basic blocks with set incoming - /// and outgoing locals. - /// Currently the control flow graph is acyclic. - pub fn new( - rng: &mut StdRng, - locals: &[SignatureToken], - parameters: &Signature, - target_blocks: BlockIDSize, - ) -> CFG { - assert!(target_blocks > 0, "The CFG must haave at least one block"); - let mut basic_blocks: HashMap = HashMap::new(); - // Generate basic blocks - for i in 0..target_blocks { - basic_blocks.insert(i, BasicBlock::new()); - } - // Generate control flow edges - let mut edges: Vec<(BlockIDSize, BlockIDSize)> = Vec::new(); - let mut block_queue: VecDeque = VecDeque::new(); - let mut current_block_id = 0; - - block_queue.push_back(current_block_id); - current_block_id += 1; - - while current_block_id < target_blocks && !block_queue.is_empty() { - let front_block = block_queue.pop_front(); - // `front_block` will be `Some` because the block queue is not empty - debug_assert!(front_block.is_some()); - let parent_block_id = front_block.unwrap(); - // The number of edges will be at most `2*target_blocks`` - // Since target blocks is at most a `u16`, this will not overflow even if - // `usize` is a `u32` - debug_assert!(edges.len() < usize::max_value()); - edges.push((parent_block_id, current_block_id)); - block_queue.push_back(current_block_id); - // `current_block_id` is bound by the max og `target_block_size` - debug_assert!(current_block_id < u16::max_value()); - current_block_id += 1; - // Generate a second child edge with prob = 1/2 - if rng.gen_bool(0.5) && current_block_id < target_blocks { - // The number of edges will be at most `2*target_blocks`` - // Since target blocks is at most a `u16`, this will not overflow even if - // `usize` is a `u32` - debug_assert!(edges.len() < usize::max_value()); - edges.push((parent_block_id, current_block_id)); - block_queue.push_back(current_block_id); - // `current_block_id` is bound by the max og `target_block_size` - debug_assert!(current_block_id < u16::max_value()); - current_block_id += 1; - } - } - - // Connect remaining blocks to return - while !block_queue.is_empty() { - let front_block = block_queue.pop_front(); - // `front_block` will be `Some` because the block queue is not empty - debug_assert!(front_block.is_some()); - let parent_block_id = front_block.unwrap(); - // By the precondition of the function - debug_assert!(target_blocks > 0); - if parent_block_id != target_blocks - 1 { - edges.push((parent_block_id, target_blocks - 1)); - } - } - debug!("Edges: {:?}", edges); - - // Build the CFG - let mut cfg = CFG { - basic_blocks, - edges, - }; - // Assign locals to basic blocks - debug_assert!(target_blocks == 0 || !cfg.basic_blocks.is_empty()); - CFG::add_locals(&mut cfg, rng, locals, parameters.0.len()); - cfg - } - - /// Get a reference to all of the basic blocks of the CFG - pub fn get_basic_blocks(&self) -> &HashMap { - &self.basic_blocks - } - - /// Get a mutable reference to all of the basic blocks of the CFG - pub fn get_basic_blocks_mut(&mut self) -> &mut HashMap { - &mut self.basic_blocks - } - - /// Retrieve the block IDs of all children of the given basic block `block_id` - pub fn get_children_ids(&self, block_id: BlockIDSize) -> Vec { - let mut children_ids: Vec = Vec::new(); - for (parent, child) in self.edges.iter() { - if *parent == block_id { - // Length is bound by iteration on `self.edges` - debug_assert!(children_ids.len() < usize::max_value()); - children_ids.push(*child); - } - } - children_ids - } - - /// Retrieve the number of children the given basic block `block_id` - pub fn num_children(&self, block_id: BlockIDSize) -> u8 { - // A `u8` is sufficient; blocks will have at most two children - self.get_children_ids(block_id).len() as u8 - } - - /// Retrieve the block IDs of all parents of the given basic block `block_id` - pub fn get_parent_ids(&self, block_id: BlockIDSize) -> Vec { - let mut parent_ids: Vec = Vec::new(); - for (parent, child) in self.edges.iter() { - if *child == block_id { - // Iteration is bound by the self.edges vector length - debug_assert!(parent_ids.len() < usize::max_value()); - parent_ids.push(*parent); - } - } - parent_ids - } - - /// Retrieve the number of parents the given basic block `block_id` - pub fn num_parents(&self, block_id: BlockIDSize) -> u8 { - // A `u8` is sufficient; blocks will have at most two children - self.get_parent_ids(block_id).len() as u8 - } - - /// Merge the outgoing locals of a set of blocks - fn merge_locals(&self, block_ids: Vec) -> BlockLocals { - assert!( - !block_ids.is_empty(), - "Cannot merge locals of empty block list" - ); - let first_basic_block = self.basic_blocks.get(&block_ids[0]); - // Implication of preconditon - debug_assert!(first_basic_block.is_some()); - let first_basic_block_locals_out = &first_basic_block.unwrap().locals_out; - let locals_len = first_basic_block_locals_out.len(); - let mut locals_out = BlockLocals::new(); - for local_index in 0..locals_len { - let abstract_value = first_basic_block_locals_out[&local_index].0.clone(); - let mut availability = BorrowState::Available; - for block_id in block_ids.iter() { - // A local is available for a block if it is available in every - // parent's outgoing locals - let basic_block = self.basic_blocks.get(block_id); - // Every block ID in the sequence should be valid - debug_assert!(basic_block.is_some()); - if basic_block.unwrap().locals_out[&local_index].1 == BorrowState::Unavailable { - availability = BorrowState::Unavailable; - } - } - locals_out.insert(local_index, (abstract_value, availability)); - } - locals_out - } - - /// Randomly vary the availability of locals - fn vary_locals(rng: &mut StdRng, locals: BlockLocals) -> BlockLocals { - let mut locals = locals; - for (_, (abstr_val, availability)) in locals.iter_mut() { - if rng.gen_bool(0.5) { - if *availability == BorrowState::Available { - *availability = BorrowState::Unavailable; - } else { - *availability = BorrowState::Available; - } - } - - if abstr_val.is_generic() { - *availability = BorrowState::Unavailable; - } - } - locals - } - - /// Add the incoming and outgoing locals for each basic block in the control flow graph. - /// Currently the incoming and outgoing locals are the same for each block. - fn add_locals(cfg: &mut CFG, rng: &mut StdRng, locals: &[SignatureToken], args_len: usize) { - debug_assert!( - !cfg.basic_blocks.is_empty(), - "Cannot add locals to empty cfg" - ); - debug!("add locals: {:#?}", locals); - for block_id in 0..cfg.basic_blocks.len() { - let cfg_copy = cfg.clone(); - let basic_block = cfg - .basic_blocks - .get_mut(&(block_id as BlockIDSize)) - .unwrap(); - if cfg_copy.num_parents(block_id as BlockIDSize) == 0 { - basic_block.locals_in = locals - .iter() - .enumerate() - .map(|(i, token)| { - let borrow_state = if i < args_len { - BorrowState::Available - } else { - BorrowState::Unavailable - }; - ( - i, - ( - AbstractValue::new_value(token.clone(), AbilitySet::PRIMITIVES), - borrow_state, - ), - ) - }) - .collect(); - } else { - // Implication of precondition - debug_assert!(!cfg_copy.basic_blocks.is_empty()); - basic_block.locals_in = - cfg_copy.merge_locals(cfg_copy.get_parent_ids(block_id as BlockIDSize)); - } - basic_block.locals_out = CFG::vary_locals(rng, basic_block.locals_in.clone()); - } - } - - /// Decide the serialization order of the blocks in the CFG - pub fn serialize_block_order(&self) -> Vec { - let mut block_order: Vec = Vec::new(); - let mut block_queue: VecDeque = VecDeque::new(); - block_queue.push_back(0); - while !block_queue.is_empty() { - let block_id_front = block_queue.pop_front(); - // The queue is non-empty so the front block id will not be none - debug_assert!(block_id_front.is_some()); - let block_id = block_id_front.unwrap(); - let child_ids = self.get_children_ids(block_id); - if child_ids.len() == 2 { - block_queue.push_front(child_ids[0]); - block_queue.push_back(child_ids[1]); - } else if child_ids.len() == 1 { - block_queue.push_back(child_ids[0]); - } else if !child_ids.is_empty() { - // We construct the CFG such that blocks have either 0, 1, or 2 - // children. - unreachable!( - "Invalid number of children for basic block {:?}", - child_ids.len() - ); - } - // This operation is expensive but is performed just when - // serializing the module. - if !block_order.contains(&block_id) { - block_order.push(block_id); - } - } - debug!("Block order: {:?}", block_order); - block_order - } - - /// Get the serialized code offset of a basic block based on its position in the serialized - /// instruction sequence. - fn get_block_offset(cfg: &CFG, block_order: &[BlockIDSize], block_id: BlockIDSize) -> u16 { - assert!( - (0..block_id).all(|id| cfg.basic_blocks.contains_key(&id)), - "Error: Invalid block_id given" - ); - let mut offset: u16 = 0; - for i in block_order { - if *i == block_id { - break; - } - if let Some(block) = cfg.basic_blocks.get(i) { - offset += block.instructions.len() as u16; - } - } - offset - } - - /// Serialize the control flow graph into a sequence of instructions. Set the offsets of branch - /// instructions appropriately. - pub fn serialize(&mut self) -> Vec { - assert!( - !self.basic_blocks.is_empty(), - "Error: CFG has no basic blocks" - ); - let cfg_copy = self.clone(); - let mut bytecode: Vec = Vec::new(); - let block_order = self.serialize_block_order(); - for block_id in &block_order { - let block = self.basic_blocks.get_mut(block_id); - // The generated block order contains every block - debug_assert!(block.is_some()); - let block = block.unwrap(); - // All basic blocks should have instructions filled in at this point - assert!( - !block.instructions.is_empty(), - "Error: block created with no instructions", - ); - let last_instruction_index = block.instructions.len() - 1; - let child_ids = cfg_copy.get_children_ids(*block_id); - if child_ids.len() == 2 { - // The left child (fallthrough) is serialized before the right (jump) - let offset = CFG::get_block_offset(&cfg_copy, &block_order, child_ids[1]); - match block.instructions.last() { - Some(Bytecode::BrTrue(_)) => { - block.instructions[last_instruction_index] = Bytecode::BrTrue(offset); - }, - Some(Bytecode::BrFalse(_)) => { - block.instructions[last_instruction_index] = Bytecode::BrFalse(offset); - }, - _ => unreachable!( - "Error: unsupported two target jump instruction, {:#?}", - block.instructions.last() - ), - }; - } else if child_ids.len() == 1 { - let offset = CFG::get_block_offset(&cfg_copy, &block_order, child_ids[0]); - match block.instructions.last() { - Some(Bytecode::Branch(_)) => { - block.instructions[last_instruction_index] = Bytecode::Branch(offset); - }, - _ => unreachable!( - "Error: unsupported one target jump instruction, {:#?}", - block.instructions.last() - ), - } - } - bytecode.extend(block.instructions.clone()); - } - debug!("Final bytecode: {:#?}", bytecode); - bytecode - } -} diff --git a/third_party/move/testing-infra/test-generation/src/error.rs b/third_party/move/testing-infra/test-generation/src/error.rs deleted file mode 100644 index 5c06c1a33808c0..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/error.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt; - -/// This struct represents an error that is returned during the -/// testcase generation process. -#[derive(Debug)] -pub struct VMError { - message: String, -} - -impl VMError { - pub fn new(message: String) -> VMError { - VMError { message } - } -} - -impl fmt::Display for VMError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.message) - } -} diff --git a/third_party/move/testing-infra/test-generation/src/lib.rs b/third_party/move/testing-infra/test-generation/src/lib.rs deleted file mode 100644 index b077021b4a197f..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/lib.rs +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -pub mod abstract_state; -pub mod borrow_graph; -pub mod bytecode_generator; -pub mod config; -pub mod control_flow_graph; -pub mod error; -pub mod summaries; -pub mod transitions; - -use crate::config::{Args, EXECUTE_UNVERIFIED_MODULE, RUN_ON_VM}; -use bytecode_generator::BytecodeGenerator; -use crossbeam_channel::{bounded, unbounded, Receiver, Sender}; -use getrandom::getrandom; -use module_generation::generate_module; -use move_binary_format::{ - access::ModuleAccess, - file_format::{ - AbilitySet, CompiledModule, FunctionDefinitionIndex, SignatureToken, StructHandleIndex, - }, -}; -use move_bytecode_verifier::verify_module; -use move_compiler::{ - compiled_unit::AnnotatedCompiledUnit, - shared::{known_attributes::KnownAttribute, Flags}, - Compiler, -}; -use move_core_types::{ - account_address::AccountAddress, - language_storage::TypeTag, - value::MoveValue, - vm_status::{StatusCode, VMStatus}, -}; -use move_vm_runtime::{ - module_traversal::*, move_vm::MoveVM, AsUnsyncModuleStorage, RuntimeEnvironment, -}; -use move_vm_test_utils::InMemoryStorage; -use move_vm_types::gas::UnmeteredGasMeter; -use rand::{rngs::StdRng, Rng, SeedableRng}; -use std::{fs, io::Write, panic, thread}; -use tracing::{debug, error, info}; - -/// This function calls the Bytecode verifier to test it -fn run_verifier(module: CompiledModule) -> Result { - match panic::catch_unwind(|| verify_module(&module)) { - Ok(res) => match res { - Ok(_) => Ok(module), - Err(err) => Err(format!("Module verification failed: {:#?}", err)), - }, - Err(err) => Err(format!("Verifier panic: {:#?}", err)), - } -} - -// Creates a storage with Move standard library as well as a few additional modules. -fn storage_with_stdlib_and_modules(additional_modules: Vec<&CompiledModule>) -> InMemoryStorage { - let mut storage = InMemoryStorage::new(); - - // First, compile and add standard library. - let (_, compiled_units) = Compiler::from_files( - move_stdlib::move_stdlib_files(), - vec![], - move_stdlib::move_stdlib_named_addresses(), - Flags::empty().set_skip_attribute_checks(true), // Not much point in checking it here. - KnownAttribute::get_all_attribute_names(), - ) - .build_and_report() - .unwrap(); - let compiled_modules = compiled_units.into_iter().map(|unit| match unit { - AnnotatedCompiledUnit::Module(annot_module) => annot_module.named_module.module, - AnnotatedCompiledUnit::Script(_) => panic!("Unexpected Script in stdlib"), - }); - for module in compiled_modules { - let mut blob = vec![]; - module.serialize(&mut blob).unwrap(); - storage.add_module_bytes(module.self_addr(), module.self_name(), blob.into()); - } - - // Now add the additional modules. - for module in additional_modules { - let mut blob = vec![]; - module.serialize(&mut blob).unwrap(); - storage.add_module_bytes(module.self_addr(), module.self_name(), blob.into()); - } - storage -} - -/// This function runs a verified module in the VM runtime -fn run_vm(module: CompiledModule) -> Result<(), VMStatus> { - // By convention the 0'th index function definition is the entrypoint to the module (i.e. that - // will contain only simply-typed arguments). - let entry_idx = FunctionDefinitionIndex::new(0); - let function_signature = { - let handle = module.function_def_at(entry_idx).function; - let sig_idx = module.function_handle_at(handle).parameters; - module.signature_at(sig_idx).clone() - }; - let main_args: Vec> = function_signature - .0 - .iter() - .map(|sig_tok| match sig_tok { - SignatureToken::Address => MoveValue::Address(AccountAddress::ZERO) - .simple_serialize() - .unwrap(), - SignatureToken::U64 => MoveValue::U64(0).simple_serialize().unwrap(), - SignatureToken::Bool => MoveValue::Bool(true).simple_serialize().unwrap(), - SignatureToken::Vector(inner_tok) if **inner_tok == SignatureToken::U8 => { - MoveValue::Vector(vec![]).simple_serialize().unwrap() - }, - SignatureToken::Vector(_) - | SignatureToken::U8 - | SignatureToken::U128 - | SignatureToken::Signer - | SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => unimplemented!("Unsupported argument type: {:#?}", sig_tok), - }) - .collect(); - - execute_function_in_module(module, entry_idx, vec![], main_args) -} - -/// Execute the first function in a module -fn execute_function_in_module( - module: CompiledModule, - idx: FunctionDefinitionIndex, - ty_args: Vec, - args: Vec>, -) -> Result<(), VMStatus> { - let module_id = module.self_id(); - let entry_name = { - let entry_func_idx = module.function_def_at(idx).function; - let entry_name_idx = module.function_handle_at(entry_func_idx).name; - module.identifier_at(entry_name_idx) - }; - { - let natives = move_stdlib::natives::all_natives( - AccountAddress::from_hex_literal("0x1").unwrap(), - move_stdlib::natives::GasParameters::zeros(), - ); - let runtime_environment = RuntimeEnvironment::new(natives); - let vm = MoveVM::new_with_runtime_environment(&runtime_environment); - - let storage = storage_with_stdlib_and_modules(vec![&module]); - let module_storage = storage.as_unsync_module_storage(runtime_environment); - - let mut sess = vm.new_session(&storage); - let traversal_storage = TraversalStorage::new(); - sess.execute_function_bypass_visibility( - &module_id, - entry_name, - ty_args, - args, - &mut UnmeteredGasMeter, - &mut TraversalContext::new(&traversal_storage), - &module_storage, - )?; - - Ok(()) - } -} - -/// Serialize a module to `path` if `output_path` is `Some(path)`. If `output_path` is `None` -/// print the module out as debug output. -fn output_error_case(module: CompiledModule, output_path: Option, case_id: u64, tid: u64) { - match output_path { - Some(path) => { - let mut out = vec![]; - module - .serialize(&mut out) - .expect("Unable to serialize module"); - let output_file = format!("{}/case{}_{}.module", path, tid, case_id); - let mut f = fs::File::create(output_file) - .unwrap_or_else(|err| panic!("Unable to open output file {}: {}", &path, err)); - f.write_all(&out) - .unwrap_or_else(|err| panic!("Unable to write to output file {}: {}", &path, err)); - }, - None => { - debug!("{:#?}", module); - }, - } -} - -fn seed(seed: Option) -> [u8; 32] { - let mut array = [0u8; 32]; - match seed { - Some(string) => { - let vec = hex::decode(string).unwrap(); - if vec.len() != 32 { - panic!("Invalid seed supplied, the length must be 32."); - } - for (i, byte) in vec.into_iter().enumerate() { - array[i] = byte; - } - }, - None => { - getrandom(&mut array).unwrap(); - }, - }; - array -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Status { - VerificationFailure, - ExecutionFailure, - Valid, -} - -fn bytecode_module(rng: &mut StdRng, module: CompiledModule) -> CompiledModule { - let mut generated_module = BytecodeGenerator::new(rng).generate_module(module.clone()); - // Module generation can retry under certain circumstances - while generated_module.is_none() { - generated_module = BytecodeGenerator::new(rng).generate_module(module.clone()); - } - generated_module.unwrap() -} - -pub fn module_frame_generation( - num_iters: Option, - seed: [u8; 32], - sender: Sender, - stats: Receiver, -) { - let mut verification_failures: u128 = 0; - let mut execution_failures: u128 = 0; - let mut generated: u128 = 1; - - let generation_options = config::module_generation_settings(); - let mut rng = StdRng::from_seed(seed); - let mut module = generate_module(&mut rng, generation_options.clone()); - // Either get the number of iterations provided by the user, or iterate "infinitely"--up to - // u128::MAX number of times. - let iters = num_iters - .map(|x| x as u128) - .unwrap_or_else(|| std::u128::MAX); - - while generated < iters && sender.send(module).is_ok() { - module = generate_module(&mut rng, generation_options.clone()); - generated += 1; - while let Ok(stat) = stats.try_recv() { - match stat { - Status::VerificationFailure => verification_failures += 1, - Status::ExecutionFailure => execution_failures += 1, - _ => (), - }; - } - - if generated > 0 && generated % 100 == 0 { - info!( - "Generated: {} Verified: {} Executed: {}", - generated, - (generated - verification_failures), - (generated - execution_failures) - ); - } - } - - // Drop the sender channel to signal to the consumers that they should expect no more modules, - // and should finish up. - drop(sender); - - // Gather final stats from the consumers. - while let Ok(stat) = stats.recv() { - match stat { - Status::VerificationFailure => verification_failures += 1, - Status::ExecutionFailure => execution_failures += 1, - _ => (), - }; - } - info!( - "Final stats: Generated: {} Verified: {} Executed: {}", - generated, - (generated - verification_failures), - (generated - execution_failures) - ); -} - -pub fn bytecode_generation( - output_path: Option, - tid: u64, - mut rng: StdRng, - receiver: Receiver, - stats: Sender, -) { - while let Ok(module) = receiver.recv() { - let mut status = Status::VerificationFailure; - debug!("Generating module"); - let module = bytecode_module(&mut rng, module); - - debug!("Done...Running module on verifier..."); - let verified_module = match run_verifier(module.clone()) { - Ok(verified_module) => { - status = Status::ExecutionFailure; - Some(verified_module) - }, - Err(e) => { - error!("{}", e); - let uid = rng.gen::(); - output_error_case(module.clone(), output_path.clone(), uid, tid); - if EXECUTE_UNVERIFIED_MODULE { - Some(module.clone()) - } else { - None - } - }, - }; - - if let Some(verified_module) = verified_module { - if RUN_ON_VM { - debug!("Done...Running module on VM..."); - let execution_result = panic::catch_unwind(|| run_vm(verified_module)); - match execution_result { - Ok(execution_result) => match execution_result { - Ok(_) => { - status = Status::Valid; - }, - Err(e) => match e.status_code() { - StatusCode::ARITHMETIC_ERROR | StatusCode::OUT_OF_GAS => { - status = Status::Valid; - }, - _ => { - error!("{}", e); - let uid = rng.gen::(); - output_error_case(module.clone(), output_path.clone(), uid, tid); - }, - }, - }, - Err(_) => { - // Save modules that cause the VM runtime to panic - let uid = rng.gen::(); - output_error_case(module.clone(), output_path.clone(), uid, tid); - }, - } - } else { - status = Status::Valid; - } - }; - stats.send(status).unwrap(); - } - - drop(stats); -} - -/// Run generate_bytecode for the range passed in and test each generated module -/// on the bytecode verifier. -pub fn run_generation(args: Args) { - let num_threads = if let Some(num_threads) = args.num_threads { - num_threads as usize - } else { - num_cpus::get() - }; - assert!( - num_threads > 0, - "Number of worker threads must be greater than 0" - ); - - let (sender, receiver) = bounded(num_threads); - let (stats_sender, stats_reciever) = unbounded(); - let seed = seed(args.seed); - - let mut threads = Vec::new(); - for tid in 0..num_threads { - let receiver = receiver.clone(); - let stats_sender = stats_sender.clone(); - let rng = StdRng::from_seed(seed); - let output_path = args.output_path.clone(); - threads.push(thread::spawn(move || { - bytecode_generation(output_path, tid as u64, rng, receiver, stats_sender) - })); - } - - // Need to drop this channel otherwise we'll get infinite blocking since the other channels are - // cloned; this one will remain open unless we close it and other threads are going to block - // waiting for more stats. - drop(stats_sender); - - let num_iters = args.num_iterations; - threads.push(thread::spawn(move || { - module_frame_generation(num_iters, seed, sender, stats_reciever) - })); - - for thread in threads { - thread.join().unwrap(); - } -} - -pub(crate) fn substitute(token: &SignatureToken, tys: &[SignatureToken]) -> SignatureToken { - use SignatureToken::*; - - match token { - Bool => Bool, - U8 => U8, - U16 => U16, - U32 => U32, - U64 => U64, - U128 => U128, - U256 => U256, - Address => Address, - Signer => Signer, - Vector(ty) => Vector(Box::new(substitute(ty, tys))), - Struct(idx) => Struct(*idx), - StructInstantiation(idx, type_params) => StructInstantiation( - *idx, - type_params.iter().map(|ty| substitute(ty, tys)).collect(), - ), - Reference(ty) => Reference(Box::new(substitute(ty, tys))), - MutableReference(ty) => MutableReference(Box::new(substitute(ty, tys))), - TypeParameter(idx) => { - // Assume that the caller has previously parsed and verified the structure of the - // file and that this guarantees that type parameter indices are always in bounds. - debug_assert!((*idx as usize) < tys.len()); - tys[*idx as usize].clone() - }, - } -} - -pub fn abilities( - module: &impl ModuleAccess, - ty: &SignatureToken, - constraints: &[AbilitySet], -) -> AbilitySet { - use SignatureToken::*; - - match ty { - Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address => AbilitySet::PRIMITIVES, - - Reference(_) | MutableReference(_) => AbilitySet::REFERENCES, - Signer => AbilitySet::SIGNER, - TypeParameter(idx) => constraints[*idx as usize], - Vector(ty) => { - AbilitySet::polymorphic_abilities(AbilitySet::VECTOR, vec![false], vec![abilities( - module, - ty, - constraints, - )]) - .unwrap() - }, - Struct(idx) => { - let sh = module.struct_handle_at(*idx); - sh.abilities - }, - StructInstantiation(idx, type_args) => { - let sh = module.struct_handle_at(*idx); - let declared_abilities = sh.abilities; - let declared_phantom_parameters = - sh.type_parameters.iter().map(|param| param.is_phantom); - let type_arguments = type_args - .iter() - .map(|arg| abilities(module, arg, constraints)); - AbilitySet::polymorphic_abilities( - declared_abilities, - declared_phantom_parameters, - type_arguments, - ) - .unwrap() - }, - } -} - -pub(crate) fn get_struct_handle_from_reference( - reference_signature: &SignatureToken, -) -> Option { - match reference_signature { - SignatureToken::Reference(signature) => match **signature { - SignatureToken::StructInstantiation(idx, _) | SignatureToken::Struct(idx) => Some(idx), - _ => None, - }, - SignatureToken::MutableReference(signature) => match **signature { - SignatureToken::StructInstantiation(idx, _) | SignatureToken::Struct(idx) => Some(idx), - _ => None, - }, - _ => None, - } -} - -pub(crate) fn get_type_actuals_from_reference( - token: &SignatureToken, -) -> Option> { - use SignatureToken::*; - - match token { - Reference(box_) | MutableReference(box_) => match &**box_ { - StructInstantiation(_, tys) => Some(tys.clone()), - Struct(_) => Some(vec![]), - _ => None, - }, - Bool - | U8 - | U64 - | U128 - | Address - | Signer - | Vector(_) - | Struct(_) - | StructInstantiation(_, _) - | TypeParameter(_) - | U16 - | U32 - | U256 => None, - } -} diff --git a/third_party/move/testing-infra/test-generation/src/main.rs b/third_party/move/testing-infra/test-generation/src/main.rs deleted file mode 100644 index 31a820a9eb2c6d..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use clap::Parser; -use test_generation::{config::Args, run_generation}; - -fn setup_log() { - tracing::subscriber::set_global_default(tracing_subscriber::FmtSubscriber::new()).unwrap(); -} - -pub fn main() { - setup_log(); - let args = Args::parse(); - run_generation(args); -} diff --git a/third_party/move/testing-infra/test-generation/src/summaries.rs b/third_party/move/testing-infra/test-generation/src/summaries.rs deleted file mode 100644 index 66077c91d2981a..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/summaries.rs +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - abstract_state::{AbstractState, AbstractValue, BorrowState, Mutability}, - error::VMError, - function_instantiation_for_state, state_control_flow, state_create_struct, - state_create_struct_from_inst, state_function_can_acquire_resource, - state_local_availability_is, state_local_exists, state_local_has_ability, state_local_place, - state_local_set, state_local_take, state_local_take_borrow, state_memory_safe, state_never, - state_register_dereference, state_stack_bin_op, state_stack_function_call, - state_stack_function_inst_call, state_stack_function_inst_popn, state_stack_function_popn, - state_stack_has, state_stack_has_ability, state_stack_has_integer, - state_stack_has_polymorphic_eq, state_stack_has_reference, state_stack_has_struct, - state_stack_has_struct_inst, state_stack_is_castable, state_stack_local_polymorphic_eq, - state_stack_pop, state_stack_push, state_stack_push_register, state_stack_push_register_borrow, - state_stack_ref_polymorphic_eq, state_stack_satisfies_function_inst_signature, - state_stack_satisfies_function_signature, state_stack_satisfies_struct_signature, - state_stack_struct_borrow_field, state_stack_struct_borrow_field_inst, - state_stack_struct_has_field, state_stack_struct_has_field_inst, state_stack_struct_inst_popn, - state_stack_struct_popn, state_stack_unpack_struct, state_stack_unpack_struct_inst, - state_struct_has_key, state_struct_inst_has_key, struct_instantiation_for_state, - transitions::*, - unpack_instantiation_for_state, with_ty_param, -}; -use move_binary_format::file_format::{ - Ability, Bytecode, FunctionHandleIndex, FunctionInstantiationIndex, SignatureToken, - StructDefInstantiationIndex, StructDefinitionIndex, -}; - -/// A `Precondition` is a boolean predicate on an `AbstractState`. -pub type Precondition = dyn Fn(&AbstractState) -> bool; - -/// A `Effect` is a function that transforms on `AbstractState` to another -pub type NonInstantiableEffect = dyn Fn(&AbstractState) -> Result; -pub type InstantiableEffect = - dyn Fn(StructDefInstantiationIndex) -> Vec>; -pub type FunctionInstantiableEffect = - dyn Fn(FunctionInstantiationIndex) -> Vec>; - -type Instantiation = dyn Fn(&AbstractState) -> (StructDefinitionIndex, Vec); -type FunctionInstantiation = dyn Fn(&AbstractState) -> (FunctionHandleIndex, Vec); -type InstantiableInstruction = dyn Fn(StructDefInstantiationIndex) -> Bytecode; -type FunctionInstantiableInstruction = dyn Fn(FunctionInstantiationIndex) -> Bytecode; - -pub enum Effects { - NoTyParams(Vec>), - TyParams( - Box, - Box, - Box, - ), - TyParamsCall( - Box, - Box, - Box, - ), -} - -/// The `Summary` of a bytecode instruction contains a list of `Precondition`s -/// and a list of `Effect`s. -pub struct Summary { - pub preconditions: Vec>, - pub effects: Effects, -} - -/// Return the `Summary` for a bytecode instruction, `instruction` -pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { - match instruction { - Bytecode::Pop => Summary { - preconditions: vec![ - state_stack_has!(0, None), - state_stack_has_ability!(0, Ability::Copy), - state_memory_safe!(Some(0)), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!()]), - }, - Bytecode::LdU8(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U8 - ))]), - }, - Bytecode::LdU16(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U16 - ))]), - }, - Bytecode::LdU32(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U32 - ))]), - }, - Bytecode::LdU64(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U64 - ))]), - }, - Bytecode::LdU128(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U128 - ))]), - }, - Bytecode::LdU256(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::U256 - ))]), - }, - Bytecode::CastU8 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U8)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U8)), - ]), - }, - Bytecode::CastU16 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U16)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U16)), - ]), - }, - Bytecode::CastU32 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U32)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U32)), - ]), - }, - Bytecode::CastU64 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U64)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U64)), - ]), - }, - Bytecode::CastU128 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U128)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U128)), - ]), - }, - Bytecode::CastU256 => Summary { - preconditions: vec![state_stack_is_castable!(SignatureToken::U256)], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::U256)), - ]), - }, - // TODO actual constant generation - Bytecode::LdConst(_) => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::Address - ))]), - }, - Bytecode::LdTrue => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::Bool, - ))]), - }, - Bytecode::LdFalse => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![state_stack_push!(AbstractValue::new_primitive( - SignatureToken::Bool - ))]), - }, - Bytecode::CopyLoc(i) => Summary { - preconditions: vec![ - state_local_exists!(i), - state_local_has_ability!(i, Ability::Copy), - state_local_availability_is!(i, BorrowState::Available), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![state_local_take!(i), state_stack_push_register!()]), - }, - Bytecode::MoveLoc(i) => Summary { - preconditions: vec![ - state_local_exists!(i), - state_local_availability_is!(i, BorrowState::Available), - // TODO: We need to track borrowing of locals. Add this in when we allow the borrow - // graph. - // state_memory_safe!(Some(i as usize)), - ], - effects: Effects::NoTyParams(vec![ - state_local_take!(i), - state_stack_push_register!(), - state_local_set!(i, BorrowState::Unavailable), - ]), - }, - Bytecode::StLoc(i) => Summary { - preconditions: vec![ - state_stack_has!(0, None), - state_local_exists!(i), - // TODO: This covers storing on an copyable local only - state_local_has_ability!(i, Ability::Drop), - state_stack_local_polymorphic_eq!(0, i as usize), - state_memory_safe!(Some(0)), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_local_place!(i), - state_local_set!(i, BorrowState::Available), - ]), - }, - Bytecode::MutBorrowLoc(i) => Summary { - // TODO: Add these back in when borrow graph is added - // preconditions: vec![ - // state_local_exists!(i), - // state_local_availability_is!(i, BorrowState::Available), - // state_memory_safe!(None), - // ], - preconditions: vec![state_never!()], - effects: Effects::NoTyParams(vec![ - state_local_take_borrow!(i, Mutability::Mutable), - state_stack_push_register!(), - ]), - }, - Bytecode::ImmBorrowLoc(i) => Summary { - // TODO: Add these back in when the borrow graph is added - //preconditions: vec![ - // state_local_exists!(i), - // state_local_availability_is!(i, BorrowState::Available), - // state_memory_safe!(None), - //], - preconditions: vec![state_never!()], - effects: Effects::NoTyParams(vec![ - state_local_take_borrow!(i, Mutability::Immutable), - state_stack_push_register!(), - ]), - }, - Bytecode::ReadRef => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Either), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_register_dereference!(), - state_stack_push_register!(), - ]), - }, - Bytecode::WriteRef => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Mutable), - state_stack_has!(1, None), - state_stack_ref_polymorphic_eq!(0, 1), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!(), state_stack_pop!()]), - }, - Bytecode::FreezeRef => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Mutable), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_register_dereference!(), - state_stack_push_register_borrow!(Mutability::Immutable), - ]), - }, - Bytecode::Add - | Bytecode::Sub - | Bytecode::Mul - | Bytecode::Div - | Bytecode::Mod - | Bytecode::BitAnd - | Bytecode::BitOr - | Bytecode::Xor => Summary { - preconditions: vec![ - state_stack_has_integer!(0), - state_stack_has_integer!(1), - state_stack_has_polymorphic_eq!(0, 1), - ], - effects: Effects::NoTyParams(vec![state_stack_bin_op!()]), - }, - Bytecode::Shl | Bytecode::Shr => Summary { - preconditions: vec![ - state_stack_has!(0, Some(AbstractValue::new_primitive(SignatureToken::U8))), - state_stack_has_integer!(1), - ], - effects: Effects::NoTyParams(vec![state_stack_bin_op!()]), - }, - Bytecode::Or | Bytecode::And => Summary { - preconditions: vec![ - state_stack_has!(0, Some(AbstractValue::new_primitive(SignatureToken::Bool))), - state_stack_has!(1, Some(AbstractValue::new_primitive(SignatureToken::Bool))), - ], - effects: Effects::NoTyParams(vec![state_stack_bin_op!()]), - }, - Bytecode::Not => Summary { - preconditions: vec![state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Bool)) - )], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::Bool)), - ]), - }, - Bytecode::Eq | Bytecode::Neq => Summary { - preconditions: vec![ - state_stack_has!(0, None), - state_stack_has!(1, None), - state_stack_has_ability!(0, Ability::Copy), - state_stack_has_polymorphic_eq!(0, 1), - state_memory_safe!(Some(0)), - state_memory_safe!(Some(1)), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::Bool)), - ]), - }, - Bytecode::Lt | Bytecode::Gt | Bytecode::Le | Bytecode::Ge => Summary { - preconditions: vec![ - state_stack_has_integer!(0), - state_stack_has_integer!(1), - state_stack_has_polymorphic_eq!(0, 1), - ], - effects: Effects::NoTyParams(vec![state_stack_bin_op!(AbstractValue::new_primitive( - SignatureToken::Bool - ))]), - }, - Bytecode::Pack(i) => Summary { - preconditions: vec![state_stack_satisfies_struct_signature!(i)], - effects: Effects::NoTyParams(vec![ - state_stack_struct_popn!(i), - state_create_struct!(i), - state_stack_push_register!(), - ]), - }, - Bytecode::PackGeneric(idx) => Summary { - preconditions: vec![state_stack_satisfies_struct_signature!(idx, exact)], - effects: Effects::TyParams( - struct_instantiation_for_state!(idx, exact), - with_ty_param!((exact, idx) => inst, - vec![ - state_stack_struct_inst_popn!(idx), - state_create_struct_from_inst!(inst), - state_stack_push_register!(), - ] - ), - with_ty_param!((exact, idx) => inst, Bytecode::PackGeneric(inst)), - ), - }, - Bytecode::Unpack(i) => Summary { - preconditions: vec![state_stack_has_struct!(i)], - effects: Effects::NoTyParams(vec![state_stack_pop!(), state_stack_unpack_struct!(i)]), - }, - Bytecode::UnpackGeneric(i) => Summary { - preconditions: vec![state_stack_has_struct_inst!(i)], - effects: Effects::TyParams( - unpack_instantiation_for_state!(), - with_ty_param!((exact, i) => inst, - vec![ - state_stack_pop!(), - state_stack_unpack_struct_inst!(inst), - ] - ), - with_ty_param!((exact, i) => inst, Bytecode::UnpackGeneric(inst)), - ), - }, - Bytecode::Exists(i) => Summary { - // The result of `state_struct_is_resource` is represented abstractly - // so concrete execution may differ - preconditions: vec![ - state_struct_has_key!(i), - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::Bool)), - ]), - }, - Bytecode::ExistsGeneric(i) => Summary { - // The result of `state_struct_is_resource` is represented abstractly - // so concrete execution may differ - preconditions: vec![ - state_struct_inst_has_key!(i), - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_push!(AbstractValue::new_primitive(SignatureToken::Bool)), - ]), - }, - Bytecode::MutBorrowField(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Mutable), - state_stack_struct_has_field!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_struct_borrow_field!(i), - ]), - }, - Bytecode::MutBorrowFieldGeneric(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Mutable), - state_stack_struct_has_field_inst!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_struct_borrow_field_inst!(i), - ]), - }, - Bytecode::ImmBorrowField(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Immutable), - state_stack_struct_has_field!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_struct_borrow_field!(i), - ]), - }, - Bytecode::ImmBorrowFieldGeneric(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(0, Mutability::Immutable), - state_stack_struct_has_field_inst!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_stack_struct_borrow_field_inst!(i), - ]), - }, - Bytecode::MutBorrowGlobal(i) => Summary { - preconditions: vec![ - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - state_struct_has_key!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct!(i), - state_stack_push_register_borrow!(Mutability::Mutable), - ]), - }, - Bytecode::MutBorrowGlobalGeneric(i) => Summary { - preconditions: vec![ - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - state_struct_inst_has_key!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct_from_inst!(i), - state_stack_push_register_borrow!(Mutability::Mutable), - ]), - }, - Bytecode::ImmBorrowGlobal(i) => Summary { - preconditions: vec![ - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - state_struct_has_key!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct!(i), - state_stack_push_register_borrow!(Mutability::Immutable), - ]), - }, - Bytecode::ImmBorrowGlobalGeneric(i) => Summary { - preconditions: vec![ - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - state_struct_inst_has_key!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct_from_inst!(i), - state_stack_push_register_borrow!(Mutability::Immutable), - ]), - }, - Bytecode::MoveFrom(i) => Summary { - preconditions: vec![ - state_function_can_acquire_resource!(), - state_struct_has_key!(i), - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct!(i), - state_stack_push_register!(), - ]), - }, - Bytecode::MoveFromGeneric(i) => Summary { - preconditions: vec![ - state_function_can_acquire_resource!(), - state_struct_inst_has_key!(i), - state_stack_has!( - 0, - Some(AbstractValue::new_primitive(SignatureToken::Address)) - ), - ], - effects: Effects::NoTyParams(vec![ - state_stack_pop!(), - state_create_struct_from_inst!(i), - state_stack_push_register!(), - ]), - }, - Bytecode::MoveTo(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(1, Mutability::Immutable), - state_struct_has_key!(i), - state_stack_has_struct!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!(), state_stack_pop!()]), - }, - Bytecode::MoveToGeneric(i) => Summary { - preconditions: vec![ - state_stack_has_reference!(1, Mutability::Immutable), - state_struct_inst_has_key!(i), - state_stack_has_struct_inst!(i), - state_memory_safe!(None), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!(), state_stack_pop!()]), - }, - Bytecode::Call(i) => Summary { - preconditions: vec![state_stack_satisfies_function_signature!(i)], - effects: Effects::NoTyParams(vec![ - state_stack_function_popn!(i), - state_stack_function_call!(i), - ]), - }, - Bytecode::CallGeneric(i) => Summary { - preconditions: vec![state_stack_satisfies_function_inst_signature!(i)], - effects: Effects::TyParamsCall( - function_instantiation_for_state!(i), - with_ty_param!((exact, i) => inst, - vec![ - state_stack_function_inst_popn!(inst), - state_stack_function_inst_call!(i), - ] - ), - with_ty_param!((exact, i) => inst, Bytecode::CallGeneric(inst)), - ), - }, - // Control flow instructions are called manually and thus have - // `state_control_flow!()` as their precondition - Bytecode::Branch(_) => Summary { - preconditions: vec![state_control_flow!()], - effects: Effects::NoTyParams(vec![]), - }, - Bytecode::BrTrue(_) => Summary { - preconditions: vec![ - state_control_flow!(), - state_stack_has!(0, Some(AbstractValue::new_primitive(SignatureToken::Bool))), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!()]), - }, - Bytecode::BrFalse(_) => Summary { - preconditions: vec![ - state_control_flow!(), - state_stack_has!(0, Some(AbstractValue::new_primitive(SignatureToken::Bool))), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!()]), - }, - Bytecode::Ret => Summary { - preconditions: vec![state_control_flow!()], - effects: Effects::NoTyParams(vec![]), - }, - Bytecode::Abort => Summary { - preconditions: vec![ - state_control_flow!(), - state_stack_has!(0, Some(AbstractValue::new_primitive(SignatureToken::U64))), - ], - effects: Effects::NoTyParams(vec![state_stack_pop!()]), - }, - Bytecode::Nop => Summary { - preconditions: vec![], - effects: Effects::NoTyParams(vec![]), - }, - // TODO: implement summaries for vector-related instructions - Bytecode::VecPack(..) - | Bytecode::VecLen(_) - | Bytecode::VecImmBorrow(_) - | Bytecode::VecMutBorrow(_) - | Bytecode::VecPushBack(_) - | Bytecode::VecPopBack(_) - | Bytecode::VecUnpack(..) - | Bytecode::VecSwap(_) => unimplemented!("Vector bytecode not supported yet"), - Bytecode::TestVariant(_) - | Bytecode::TestVariantGeneric(_) - | Bytecode::PackVariant(_) - | Bytecode::PackVariantGeneric(_) - | Bytecode::UnpackVariant(_) - | Bytecode::UnpackVariantGeneric(_) - | Bytecode::MutBorrowVariantField(_) - | Bytecode::MutBorrowVariantFieldGeneric(_) - | Bytecode::ImmBorrowVariantField(_) - | Bytecode::ImmBorrowVariantFieldGeneric(_) => { - // TODO(#13806): consider implementing for struct variants - unimplemented!("Enum types bytecode not supported yet") - }, - } -} diff --git a/third_party/move/testing-infra/test-generation/src/transitions.rs b/third_party/move/testing-infra/test-generation/src/transitions.rs deleted file mode 100644 index 18f592c470902e..00000000000000 --- a/third_party/move/testing-infra/test-generation/src/transitions.rs +++ /dev/null @@ -1,1555 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - abilities, - abstract_state::{AbstractState, AbstractValue, BorrowState, Mutability}, - config::ALLOW_MEMORY_UNSAFE, - error::VMError, - get_struct_handle_from_reference, get_type_actuals_from_reference, substitute, -}; -use move_binary_format::{ - access::*, - file_format::{ - Ability, AbilitySet, FieldHandleIndex, FieldInstantiationIndex, FunctionHandleIndex, - FunctionInstantiationIndex, Signature, SignatureIndex, SignatureToken, - StructDefInstantiationIndex, StructDefinitionIndex, StructFieldInformation, TableIndex, - }, - views::{FunctionHandleView, StructDefinitionView, ViewInternals}, -}; -use std::collections::{hash_map::Entry, HashMap}; - -//--------------------------------------------------------------------------- -// Type Instantiations from Unification with the Abstract Stack -//--------------------------------------------------------------------------- - -/// A substitution is a mapping from type formal index to the `SignatureToken` representing the -/// type instantiation for that index. -#[derive(Default)] -pub struct Subst { - pub subst: HashMap, -} - -impl Subst { - pub fn new() -> Self { - Default::default() - } - - /// NB that the position of arguments here matters. We can build a substitution if the `instr_sig` - /// is a type parameter, and the `stack_sig` is a concrete type. But, if the instruction signature is a - /// concrete type, but the stack signature is a type parameter, they cannot unify and no - /// substitution is created. - pub fn check_and_add(&mut self, stack_sig: SignatureToken, instr_sig: SignatureToken) -> bool { - match (stack_sig, instr_sig) { - (tok, SignatureToken::TypeParameter(idx)) => { - if let Some(other_type) = self.subst.get(&(idx as usize)).cloned() { - // If we have already defined a subtitution for this type parameter, then make - // sure the signature token on the stack is amenable with the type selection. - tok == other_type - } else { - // Otherwise record that the type parameter maps to this signature token. - self.subst.insert(idx as usize, tok); - true - } - }, - // A type parameter on the stack _cannot_ be unified with a non type parameter. But - // that case has already been taken care of above. This case is added for explicitness, - // but it could be rolled into the catch-all at the bottom of this match. - (SignatureToken::TypeParameter(_), _) => false, - (SignatureToken::Struct(sig1), SignatureToken::Struct(sig2)) => sig1 == sig2, - // Build a substitution from recursing into structs - ( - SignatureToken::StructInstantiation(sig1, params1), - SignatureToken::StructInstantiation(sig2, params2), - ) => { - if sig1 != sig2 { - return false; - } - assert!(params1.len() == params2.len()); - for (s1, s2) in params1.into_iter().zip(params2.into_iter()) { - if !self.check_and_add(s1, s2) { - return false; - } - } - true - }, - (x, y) => x == y, - } - } - - /// Return the instantiation from the substitution that has been built. - pub fn instantiation(self) -> Vec { - let mut vec = self.subst.into_iter().collect::>(); - vec.sort_by(|a, b| a.0.cmp(&b.0)); - vec.into_iter().map(|x| x.1).collect() - } -} - -//--------------------------------------------------------------------------- -// Kind Operations -//--------------------------------------------------------------------------- - -/// Given a signature token, returns the abilities of this token in the module context, and -/// instantiation for the function. -pub fn abilities_for_token( - state: &AbstractState, - token: &SignatureToken, - type_paramters: &[AbilitySet], -) -> AbilitySet { - abilities(&state.module.module, token, type_paramters) -} - -/// Given a locals signature index, determine the abilities for each signature token. Restricted for -/// determining abilities at the top-level only. This is reflected in the use of -/// `state.instantiation[..]` as the kind context. -pub fn abilities_for_instantiation( - state: &AbstractState, - instantiation: &[SignatureToken], -) -> Vec { - instantiation - .iter() - .map(|token| abilities(&state.module.module, token, &state.instantiation[..])) - .collect() -} - -/// Determine whether the stack contains an integer value at given index. -pub fn stack_has_integer(state: &AbstractState, index: usize) -> bool { - index < state.stack_len() - && match state.stack_peek(index) { - Some(AbstractValue { token, .. }) => token.is_integer(), - None => false, - } -} - -pub fn stack_top_is_castable_to(state: &AbstractState, typ: SignatureToken) -> bool { - stack_has_integer(state, 0) - && match typ { - SignatureToken::U8 => stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ), - SignatureToken::U64 => { - stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U16)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U32)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U64)), - ) - }, - SignatureToken::U16 => { - stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U16)), - ) - }, - SignatureToken::U32 => { - stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U16)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U32)), - ) - }, - SignatureToken::U128 => { - stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U16)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U32)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U64)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U128)), - ) - }, - SignatureToken::U256 => { - stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U8)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U16)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U32)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U64)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U128)), - ) || stack_has( - state, - 0, - Some(AbstractValue::new_primitive(SignatureToken::U256)), - ) - }, - SignatureToken::Bool - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::TypeParameter(_) => false, - } -} - -/// Determine the abstract value at `index` is of the given kind, if it exists. -/// If it does not exist, return `false`. -pub fn stack_has_ability(state: &AbstractState, index: usize, ability: Ability) -> bool { - if index < state.stack_len() { - match state.stack_peek(index) { - Some(abstract_value) => { - return abstract_value.abilities.has_ability(ability); - }, - None => return false, - } - } - false -} - -pub fn stack_has_all_abilities(state: &AbstractState, index: usize, abilities: AbilitySet) -> bool { - if !stack_has(state, index, None) { - return false; - } - let stack_value = state.stack_peek(index).unwrap(); - abilities.is_subset(stack_value.abilities) -} - -/// Check whether the local at `index` has the given ability -pub fn local_has_ability(state: &AbstractState, index: u8, ability: Ability) -> bool { - state - .local_has_ability(index as usize, ability) - .unwrap_or(false) -} - -//--------------------------------------------------------------------------- -// Stack & Local Predicates -//--------------------------------------------------------------------------- - -/// Determine whether the stack is at least of size `index`. If the optional `abstract_value` -/// argument is some `AbstractValue`, check whether the type at `index` is that abstract_value. -pub fn stack_has( - state: &AbstractState, - index: usize, - abstract_value: Option, -) -> bool { - match abstract_value { - Some(abstract_value) => { - index < state.stack_len() && state.stack_peek(index) == Some(abstract_value) - }, - None => index < state.stack_len(), - } -} - -/// Determine whether two tokens on the stack have the same type -pub fn stack_has_polymorphic_eq(state: &AbstractState, index1: usize, index2: usize) -> bool { - if stack_has(state, index2, None) { - state.stack_peek(index1) == state.stack_peek(index2) - && stack_has_ability(state, index1, Ability::Drop) - } else { - false - } -} - -/// Determine whether an abstract value on the stack and a abstract value in the locals have the -/// same type -pub fn stack_local_polymorphic_eq(state: &AbstractState, index1: usize, index2: usize) -> bool { - if stack_has(state, index1, None) { - if let Some((abstract_value, _)) = state.local_get(index2) { - return state.stack_peek(index1) == Some(abstract_value.clone()); - } - } - false -} - -/// Check whether the local at `index` exists -pub fn local_exists(state: &AbstractState, index: u8) -> bool { - state.local_exists(index as usize) -} - -/// Check whether the local at `index` is of the given availability -pub fn local_availability_is(state: &AbstractState, index: u8, availability: BorrowState) -> bool { - state - .local_availability_is(index as usize, availability) - .unwrap_or(false) -} - -/// Determine whether an abstract value on the stack that is a reference points to something of the -/// same type as another abstract value on the stack -pub fn stack_ref_polymorphic_eq(state: &AbstractState, index1: usize, index2: usize) -> bool { - if stack_has(state, index2, None) { - if let Some(abstract_value) = state.stack_peek(index1) { - match abstract_value.token { - SignatureToken::MutableReference(token) | SignatureToken::Reference(token) => { - let abstract_value_inner = AbstractValue { - token: (*token).clone(), - abilities: abilities_for_token(state, &token, &state.instantiation[..]), - }; - return Some(abstract_value_inner) == state.stack_peek(index2); - }, - SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => return false, - } - } - } - false -} - -//--------------------------------------------------------------------------- -// Stack and Local Operations -//--------------------------------------------------------------------------- - -/// Pop from the top of the stack. -pub fn stack_pop(state: &AbstractState) -> Result { - let mut state = state.clone(); - state.stack_pop()?; - Ok(state) -} - -pub enum StackBinOpResult { - Left, - Right, - Other(AbstractValue), -} - -/// Perform a binary operation using the top two values on the stack as operands. -pub fn stack_bin_op( - state: &AbstractState, - res: StackBinOpResult, -) -> Result { - let mut state = state.clone(); - let right = { - state.stack_pop()?; - state.register_move().unwrap() - }; - let left = { - state.stack_pop()?; - state.register_move().unwrap() - }; - state.stack_push(match res { - StackBinOpResult::Left => left, - StackBinOpResult::Right => right, - StackBinOpResult::Other(val) => val, - }); - Ok(state) -} - -/// Push given abstract_value to the top of the stack. -pub fn stack_push( - state: &AbstractState, - abstract_value: AbstractValue, -) -> Result { - let mut state = state.clone(); - state.stack_push(abstract_value); - Ok(state) -} - -/// Push to the top of the stack from the register. -pub fn stack_push_register(state: &AbstractState) -> Result { - let mut state = state.clone(); - state.stack_push_register()?; - Ok(state) -} - -/// Set the availability of local at `index` -pub fn local_set( - state: &AbstractState, - index: u8, - availability: BorrowState, -) -> Result { - let mut state = state.clone(); - state.local_set(index as usize, availability)?; - Ok(state) -} - -/// Put copy of the local at `index` in register -pub fn local_take(state: &AbstractState, index: u8) -> Result { - let mut state = state.clone(); - state.local_take(index as usize)?; - Ok(state) -} - -/// Put reference to local at `index` in register -pub fn local_take_borrow( - state: &AbstractState, - index: u8, - mutability: Mutability, -) -> Result { - let mut state = state.clone(); - state.local_take_borrow(index as usize, mutability)?; - Ok(state) -} - -/// Insert the register value into the locals at `index` -pub fn local_place(state: &AbstractState, index: u8) -> Result { - let mut state = state.clone(); - state.local_place(index as usize)?; - Ok(state) -} - -//--------------------------------------------------------------------------- -// Struct Predicates and Operations -//--------------------------------------------------------------------------- - -pub fn stack_satisfies_struct_instantiation( - state: &AbstractState, - struct_index: StructDefInstantiationIndex, - exact: bool, -) -> (bool, Subst) { - let struct_inst = state.module.module.struct_instantiation_at(struct_index); - if exact { - stack_satisfies_struct_signature(state, struct_inst.def, Some(struct_inst.type_parameters)) - } else { - stack_satisfies_struct_signature(state, struct_inst.def, None) - } -} - -/// Determine whether the struct at the given index can be constructed from the values on -/// the stack. -/// Note that this function is bidirectional; if there is an instantiation, we check it. Otherwise, -/// we infer the types that are needed. -pub fn stack_satisfies_struct_signature( - state: &AbstractState, - struct_index: StructDefinitionIndex, - instantiation: Option, -) -> (bool, Subst) { - let instantiation = instantiation.map(|index| state.module.instantiantiation_at(index)); - let struct_def = state.module.module.struct_def_at(struct_index); - let struct_def = StructDefinitionView::new(&state.module.module, struct_def); - // Get the type formals for the struct, and the kinds that they expect. - let type_parameters = struct_def.type_parameters(); - let field_token_views = struct_def - .fields() - .into_iter() - .flatten() - .map(|field| field.type_signature().token()); - let mut satisfied = true; - let mut substitution = Subst::new(); - for (i, token_view) in field_token_views.rev().enumerate() { - let ty = if let Some(subst) = &instantiation { - substitute(token_view.as_inner(), subst) - } else { - token_view.as_inner().clone() - }; - let has = if let SignatureToken::TypeParameter(idx) = &ty { - if stack_has_all_abilities(state, i, type_parameters[*idx as usize].constraints) { - let stack_tok = state.stack_peek(i).unwrap(); - substitution.check_and_add(stack_tok.token, ty) - } else { - false - } - } else { - let abstract_value = AbstractValue { - token: ty, - abilities: abilities( - &state.module.module, - token_view.as_inner(), - &type_parameters - .iter() - .map(|param| param.constraints) - .collect::>(), - ), - }; - stack_has(state, i, Some(abstract_value)) - }; - - if !has { - satisfied = false; - } - } - (satisfied, substitution) -} - -pub fn get_struct_instantiation_for_state( - state: &AbstractState, - struct_inst_idx: StructDefInstantiationIndex, - exact: bool, -) -> (StructDefinitionIndex, Vec) { - let struct_inst = state.module.struct_instantiantiation_at(struct_inst_idx); - if exact { - return ( - struct_inst.def, - state - .module - .instantiantiation_at(struct_inst.type_parameters) - .clone(), - ); - } - let struct_index = struct_inst.def; - let mut partial_instantiation = stack_satisfies_struct_signature(state, struct_index, None).1; - let struct_def = state.module.module.struct_def_at(struct_index); - let struct_def = StructDefinitionView::new(&state.module.module, struct_def); - let typs = struct_def.type_parameters(); - for (index, type_param) in typs.iter().enumerate() { - if let Entry::Vacant(e) = partial_instantiation.subst.entry(index) { - if type_param.constraints.has_key() { - unimplemented!("[Struct Instantiation] Need to fill in resource type params"); - } else { - e.insert(SignatureToken::U64); - } - } - } - (struct_index, partial_instantiation.instantiation()) -} - -/// Determine if a struct (of the given signature) is at the top of the stack -/// The `struct_index` can be `Some(index)` to check for a particular struct, -/// or `None` to just check that there is a a struct. -pub fn stack_has_struct(state: &AbstractState, struct_index: StructDefinitionIndex) -> bool { - if state.stack_len() > 0 { - if let Some(struct_value) = state.stack_peek(0) { - match struct_value.token { - SignatureToken::Struct(struct_handle) - | SignatureToken::StructInstantiation(struct_handle, _) => { - let struct_def = state.module.module.struct_def_at(struct_index); - return struct_handle == struct_def.struct_handle; - }, - SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => return false, - } - } - } - false -} - -pub fn stack_has_struct_inst( - state: &AbstractState, - struct_index: StructDefInstantiationIndex, -) -> bool { - let struct_inst = state.module.module.struct_instantiation_at(struct_index); - stack_has_struct(state, struct_inst.def) -} - -/// Determine if a struct at the given index is a resource -pub fn struct_abilities( - state: &AbstractState, - struct_index: StructDefinitionIndex, - type_args: &Signature, -) -> AbilitySet { - let struct_def = state.module.module.struct_def_at(struct_index); - let struct_handle = state - .module - .module - .struct_handle_at(struct_def.struct_handle); - let declared_phantom_parameters = struct_handle - .type_parameters - .iter() - .map(|param| param.is_phantom); - let type_argument_abilities = abilities_for_instantiation(state, &type_args.0); - AbilitySet::polymorphic_abilities( - struct_handle.abilities, - declared_phantom_parameters, - type_argument_abilities, - ) - .unwrap() -} - -pub fn struct_inst_abilities( - state: &AbstractState, - struct_index: StructDefInstantiationIndex, -) -> AbilitySet { - let struct_inst = state.module.module.struct_instantiation_at(struct_index); - let type_args = state - .module - .module - .signature_at(struct_inst.type_parameters); - struct_abilities(state, struct_inst.def, type_args) -} - -pub fn stack_struct_has_field_inst( - state: &AbstractState, - field_index: FieldInstantiationIndex, -) -> bool { - let field_inst = state.module.module.field_instantiation_at(field_index); - stack_struct_has_field(state, field_inst.handle) -} - -pub fn stack_struct_has_field(state: &AbstractState, field_index: FieldHandleIndex) -> bool { - let field_handle = state.module.module.field_handle_at(field_index); - if let Some(struct_handle_index) = state - .stack_peek(0) - .and_then(|abstract_value| get_struct_handle_from_reference(&abstract_value.token)) - { - let struct_def = state.module.module.struct_def_at(field_handle.owner); - return struct_handle_index == struct_def.struct_handle; - } - false -} - -/// Determine whether the stack has a reference at `index` with the given mutability. -/// If `mutable` is `Either` then the reference can be either mutable or immutable -pub fn stack_has_reference(state: &AbstractState, index: usize, mutability: Mutability) -> bool { - if state.stack_len() > index { - if let Some(abstract_value) = state.stack_peek(index) { - match abstract_value.token { - SignatureToken::MutableReference(_) => { - if mutability == Mutability::Mutable || mutability == Mutability::Either { - return true; - } - }, - SignatureToken::Reference(_) => { - if mutability == Mutability::Immutable || mutability == Mutability::Either { - return true; - } - }, - SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => return false, - } - } - } - false -} - -pub fn stack_struct_inst_popn( - state: &AbstractState, - struct_inst_index: StructDefInstantiationIndex, -) -> Result { - let struct_inst = state - .module - .module - .struct_instantiation_at(struct_inst_index); - stack_struct_popn(state, struct_inst.def) -} - -/// Pop the number of stack values required to construct the struct -/// at `struct_index` -pub fn stack_struct_popn( - state: &AbstractState, - struct_index: StructDefinitionIndex, -) -> Result { - let state_copy = state.clone(); - let mut state = state.clone(); - let struct_def = state_copy.module.module.struct_def_at(struct_index); - let struct_def_view = StructDefinitionView::new(&state_copy.module.module, struct_def); - for _ in struct_def_view.fields().unwrap() { - state.stack_pop()?; - } - Ok(state) -} - -pub fn create_struct_from_inst( - state: &AbstractState, - struct_index: StructDefInstantiationIndex, -) -> Result { - let struct_inst = state.module.module.struct_instantiation_at(struct_index); - create_struct(state, struct_inst.def, Some(struct_inst.type_parameters)) -} - -/// Construct a struct from abstract values on the stack -/// The struct is stored in the register after creation -pub fn create_struct( - state: &AbstractState, - struct_index: StructDefinitionIndex, - instantiation: Option, -) -> Result { - let state_copy = state.clone(); - let mut state = state.clone(); - let struct_def = state_copy.module.module.struct_def_at(struct_index); - // Get the type, and kind of this struct - let sig_tok = match instantiation { - None => SignatureToken::Struct(struct_def.struct_handle), - Some(inst) => { - let ty_instantiation = state.module.instantiantiation_at(inst); - SignatureToken::StructInstantiation(struct_def.struct_handle, ty_instantiation.clone()) - }, - }; - let struct_kind = abilities_for_token(&state, &sig_tok, &state.instantiation); - let struct_value = AbstractValue::new_struct(sig_tok, struct_kind); - state.register_set(struct_value); - Ok(state) -} - -pub fn stack_unpack_struct_instantiation( - state: &AbstractState, -) -> (StructDefinitionIndex, Vec) { - if let Some(av) = state.stack_peek(0) { - match av.token { - SignatureToken::StructInstantiation(handle, toks) => { - let mut def_filter = state - .module - .module - .struct_defs() - .iter() - .enumerate() - .filter(|(_, struct_def)| struct_def.struct_handle == handle); - match def_filter.next() { - Some((idx, _)) => (StructDefinitionIndex(idx as TableIndex), toks), - None => panic!("Invalid unpack -- non-struct def value found at top of stack"), - } - }, - SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Struct(_) - | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => { - panic!("Invalid unpack -- non-struct value found at top of stack") - }, - } - } else { - panic!("Invalid unpack -- precondition not satisfied"); - } -} - -pub fn stack_unpack_struct_inst( - state: &AbstractState, - struct_index: StructDefInstantiationIndex, -) -> Result { - let struct_inst = state.module.module.struct_instantiation_at(struct_index); - stack_unpack_struct(state, struct_inst.def, Some(struct_inst.type_parameters)) -} - -/// Push the fields of a struct as `AbstractValue`s to the stack -pub fn stack_unpack_struct( - state: &AbstractState, - struct_index: StructDefinitionIndex, - instantiation: Option, -) -> Result { - let state_copy = state.clone(); - let mut state = state.clone(); - let ty_instantiation = match instantiation { - Some(inst) => state.module.instantiantiation_at(inst).clone(), - None => vec![], - }; - let abilities = abilities_for_instantiation(&state_copy, &ty_instantiation); - let struct_def = state_copy.module.module.struct_def_at(struct_index); - let struct_def_view = StructDefinitionView::new(&state_copy.module.module, struct_def); - let token_views = struct_def_view - .fields() - .into_iter() - .flatten() - .map(|field| field.type_signature().token()); - for token_view in token_views { - let abstract_value = AbstractValue { - token: substitute(token_view.as_inner(), &ty_instantiation), - abilities: abilities_for_token(&state, token_view.as_inner(), &abilities), - }; - state = stack_push(&state, abstract_value)?; - } - Ok(state) -} - -pub fn struct_ref_instantiation(state: &mut AbstractState) -> Result, VMError> { - let token = state.register_move().unwrap().token; - if let Some(type_actuals) = get_type_actuals_from_reference(&token) { - Ok(type_actuals) - } else { - Err(VMError::new("Invalid field borrow".to_string())) - } -} - -/// Push the field at `field_index` of a struct as an `AbstractValue` to the stack -pub fn stack_struct_borrow_field( - state: &AbstractState, - field_index: FieldHandleIndex, -) -> Result { - let mut state = state.clone(); - let typs = struct_ref_instantiation(&mut state)?; - let abilities = abilities_for_instantiation(&state, &typs); - let field_handle = state.module.module.field_handle_at(field_index); - let struct_def = state.module.module.struct_def_at(field_handle.owner); - let field_signature = match &struct_def.field_information { - StructFieldInformation::Native => { - return Err(VMError::new("Borrow field on a native struct".to_string())); - }, - StructFieldInformation::DeclaredVariants(..) => { - // TODO(#13806): consider implementing for struct variants - return Err(VMError::new("Enum types not yet supported".to_string())); - }, - StructFieldInformation::Declared(fields) => { - let field_def = &fields[field_handle.field as usize]; - &field_def.signature.0 - }, - }; - let reified_field_sig = substitute(field_signature, &typs); - // NB: We determine the kind on the non-reified_field_sig; we want any local references to - // type parameters to point to (struct) local type parameters. We could possibly also use the - // reified_field_sig coupled with the top-level instantiation, but I need to convince myself of - // the correctness of this. - let abstract_value = AbstractValue { - token: SignatureToken::MutableReference(Box::new(reified_field_sig)), - abilities: abilities_for_token(&state, field_signature, &abilities), - }; - state = stack_push(&state, abstract_value)?; - Ok(state) -} - -pub fn stack_struct_borrow_field_inst( - state: &AbstractState, - field_index: FieldInstantiationIndex, -) -> Result { - let field_inst = state.module.module.field_instantiation_at(field_index); - stack_struct_borrow_field(state, field_inst.handle) -} - -/// Dereference the value stored in the register. If the value is not a reference, or -/// the register is empty, return an error. -pub fn register_dereference(state: &AbstractState) -> Result { - let mut state = state.clone(); - if let Some(abstract_value) = state.register_move() { - match abstract_value.token { - SignatureToken::MutableReference(token) => { - state.register_set(AbstractValue { - token: *token, - abilities: abstract_value.abilities, - }); - Ok(state) - }, - SignatureToken::Reference(token) => { - state.register_set(AbstractValue { - token: *token, - abilities: abstract_value.abilities, - }); - Ok(state) - }, - SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::Signer - | SignatureToken::Vector(_) - | SignatureToken::Struct(_) - | SignatureToken::StructInstantiation(_, _) - | SignatureToken::TypeParameter(_) - | SignatureToken::U16 - | SignatureToken::U32 - | SignatureToken::U256 => Err(VMError::new( - "Register does not contain a reference".to_string(), - )), - } - } else { - println!("{:?}", state); - Err(VMError::new("Register is empty".to_string())) - } -} - -/// Push a reference to a register value with the given mutability. -pub fn stack_push_register_borrow( - state: &AbstractState, - mutability: Mutability, -) -> Result { - let mut state = state.clone(); - if let Some(abstract_value) = state.register_move() { - match mutability { - Mutability::Mutable => { - state.stack_push(AbstractValue { - token: SignatureToken::MutableReference(Box::new(abstract_value.token)), - abilities: abstract_value.abilities, - }); - Ok(state) - }, - Mutability::Immutable => { - state.stack_push(AbstractValue { - token: SignatureToken::Reference(Box::new(abstract_value.token)), - abilities: abstract_value.abilities, - }); - Ok(state) - }, - Mutability::Either => Err(VMError::new("Mutability must be specified".to_string())), - } - } else { - Err(VMError::new("Register is empty".to_string())) - } -} - -//--------------------------------------------------------------------------- -// Function Call Predicates and Operations -//--------------------------------------------------------------------------- - -/// Determine whether the function at the given index can be constructed from the values on -/// the stack. -pub fn stack_satisfies_function_signature( - state: &AbstractState, - function_index: FunctionHandleIndex, -) -> (bool, Subst) { - let state_copy = state.clone(); - let function_handle = state_copy.module.module.function_handle_at(function_index); - let type_parameters = &function_handle.type_parameters; - let mut satisfied = true; - let mut substitution = Subst::new(); - let parameters = &state_copy.module.module.signatures()[function_handle.parameters.0 as usize]; - for (i, parameter) in parameters.0.iter().rev().enumerate() { - let has = if let SignatureToken::TypeParameter(idx) = parameter { - if stack_has_all_abilities(state, i, type_parameters[*idx as usize]) { - let stack_tok = state.stack_peek(i).unwrap(); - substitution.check_and_add(stack_tok.token, parameter.clone()) - } else { - false - } - } else { - let abilities = abilities(&state.module.module, parameter, type_parameters); - let abstract_value = AbstractValue { - token: parameter.clone(), - abilities, - }; - stack_has(state, i, Some(abstract_value)) - }; - if !has { - satisfied = false; - } - } - (satisfied, substitution) -} - -pub fn stack_satisfies_function_inst_signature( - state: &AbstractState, - function_index: FunctionInstantiationIndex, -) -> (bool, Subst) { - let func_inst = state - .module - .module - .function_instantiation_at(function_index); - stack_satisfies_function_signature(state, func_inst.handle) -} - -/// Whether the function acquires any global resources or not -pub fn function_can_acquire_resource(state: &AbstractState) -> bool { - !state.acquires_global_resources.is_empty() -} - -/// Simulate calling the function at `function_index` -pub fn stack_function_call( - state: &AbstractState, - function_index: FunctionHandleIndex, - instantiation: Option, -) -> Result { - let state_copy = state.clone(); - let mut state = state.clone(); - let function_handle = state_copy.module.module.function_handle_at(function_index); - let return_ = &state_copy.module.module.signatures()[function_handle.return_.0 as usize]; - let mut ty_instantiation = &vec![]; - if let Some(inst) = instantiation { - ty_instantiation = state_copy.module.instantiantiation_at(inst) - } - let abilities = abilities_for_instantiation(&state_copy, ty_instantiation); - for return_type in return_.0.iter() { - let abstract_value = AbstractValue { - token: substitute(return_type, ty_instantiation), - abilities: abilities_for_token(&state, return_type, &abilities), - }; - state = stack_push(&state, abstract_value)?; - } - Ok(state) -} - -pub fn stack_function_inst_call( - state: &AbstractState, - function_index: FunctionInstantiationIndex, -) -> Result { - let func_inst = state - .module - .module - .function_instantiation_at(function_index); - stack_function_call(state, func_inst.handle, Some(func_inst.type_parameters)) -} - -pub fn get_function_instantiation_for_state( - state: &AbstractState, - function_index: FunctionInstantiationIndex, -) -> (FunctionHandleIndex, Vec) { - let func_inst = state - .module - .module - .function_instantiation_at(function_index); - let mut partial_instantiation = stack_satisfies_function_signature(state, func_inst.handle).1; - let function_handle = state.module.module.function_handle_at(func_inst.handle); - let function_handle = FunctionHandleView::new(&state.module.module, function_handle); - let typs = function_handle.type_parameters(); - for (index, abilities) in typs.iter().enumerate() { - if let Entry::Vacant(e) = partial_instantiation.subst.entry(index) { - if abilities.has_key() { - unimplemented!("[Struct Instantiation] Need to fill in resource type params"); - } else { - e.insert(SignatureToken::U64); - } - } - } - (func_inst.handle, partial_instantiation.instantiation()) -} - -/// Pop the number of stack values required to call the function -/// at `function_index` -pub fn stack_function_popn( - state: &AbstractState, - function_index: FunctionHandleIndex, -) -> Result { - let state_copy = state.clone(); - let mut state = state.clone(); - let function_handle = state_copy.module.module.function_handle_at(function_index); - let parameters = &state_copy.module.module.signatures()[function_handle.parameters.0 as usize]; - let number_of_pops = parameters.0.iter().len(); - for _ in 0..number_of_pops { - state.stack_pop()?; - } - Ok(state) -} - -pub fn stack_function_inst_popn( - state: &AbstractState, - function_index: FunctionInstantiationIndex, -) -> Result { - let func_inst = state - .module - .module - .function_instantiation_at(function_index); - stack_function_popn(state, func_inst.handle) -} - -/// TODO: This is a temporary function that represents memory -/// safety for a reference. This should be removed and replaced -/// with appropriate memory safety premises when the borrow checking -/// infrastructure is fully implemented. -/// `index` is `Some(i)` if the instruction can be memory safe when operating -/// on non-reference types. -pub fn memory_safe(state: &AbstractState, index: Option) -> bool { - match index { - Some(index) => { - if stack_has_reference(state, index, Mutability::Either) { - ALLOW_MEMORY_UNSAFE - } else { - true - } - }, - None => ALLOW_MEMORY_UNSAFE, - } -} - -//--------------------------------------------------------------------------- -// Macros -//--------------------------------------------------------------------------- - -/// Wrapper for enclosing the arguments of `stack_has` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_has { - ($e1:expr, $e2:expr) => { - Box::new(move |state| stack_has(state, $e1, $e2)) - }; -} - -/// Determines if the type at the top of the abstract stack is castable to the given type. -#[macro_export] -macro_rules! state_stack_is_castable { - ($e1:expr) => { - Box::new(move |state| stack_top_is_castable_to(state, $e1)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_has_integer` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_has_integer { - ($e1:expr) => { - Box::new(move |state| stack_has_integer(state, $e1)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_kind_is` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_has_ability { - ($e:expr, $a:expr) => { - Box::new(move |state| stack_has_ability(state, $e, $a)) - }; -} - -/// Wrapper for for enclosing the arguments of `stack_has_polymorphic_eq` so that only the `state` -/// needs to be given. -#[macro_export] -macro_rules! state_stack_has_polymorphic_eq { - ($e1:expr, $e2:expr) => { - Box::new(move |state| stack_has_polymorphic_eq(state, $e1, $e2)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_pop` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_pop { - () => { - Box::new(move |state| stack_pop(state)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_push` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_push { - ($e:expr) => { - Box::new(move |state| stack_push(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_push_register` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_stack_push_register { - () => { - Box::new(move |state| stack_push_register(state)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_local_polymorphic_eq` so that only the `state` -/// needs to be given. -#[macro_export] -macro_rules! state_stack_local_polymorphic_eq { - ($e1:expr, $e2:expr) => { - Box::new(move |state| stack_local_polymorphic_eq(state, $e1, $e2)) - }; -} - -/// Wrapper for enclosing the arguments of `local_exists` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_exists { - ($e:expr) => { - Box::new(move |state| local_exists(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `local_availability_is` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_availability_is { - ($e:expr, $a:expr) => { - Box::new(move |state| local_availability_is(state, $e, $a)) - }; -} - -/// Wrapper for enclosing the arguments of `state_local_has_ability` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_has_ability { - ($e:expr, $a:expr) => { - Box::new(move |state| local_has_ability(state, $e, $a)) - }; -} - -/// Wrapper for enclosing the arguments of `local_set` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_set { - ($e:expr, $a:expr) => { - Box::new(move |state| local_set(state, $e, $a)) - }; -} - -/// Wrapper for enclosing the arguments of `local_take` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_take { - ($e:expr) => { - Box::new(move |state| local_take(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `local_take_borrow` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_take_borrow { - ($e:expr, $mutable:expr) => { - Box::new(move |state| local_take_borrow(state, $e, $mutable)) - }; -} - -/// Wrapper for enclosing the arguments of `local_palce` so that only the `state` needs -/// to be given. -#[macro_export] -macro_rules! state_local_place { - ($e:expr) => { - Box::new(move |state| local_place(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_ref_polymorphic_eq` so that only the `state` -/// needs to be given. -#[macro_export] -macro_rules! state_stack_ref_polymorphic_eq { - ($e1:expr, $e2:expr) => { - Box::new(move |state| stack_ref_polymorphic_eq(state, $e1, $e2)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_satisfies_struct_signature` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_satisfies_struct_signature { - ($e:expr) => { - Box::new(move |state| stack_satisfies_struct_signature(state, $e, None).0) - }; - ($e:expr, $is_exact:expr) => { - Box::new(move |state| stack_satisfies_struct_instantiation(state, $e, $is_exact).0) - }; -} - -/// Wrapper for enclosing the arguments of `state_stack_struct_inst_popn` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_struct_inst_popn { - ($e:expr) => { - Box::new(move |state| stack_struct_inst_popn(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_struct_popn` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_struct_popn { - ($e:expr) => { - Box::new(move |state| stack_struct_popn(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_pack_struct` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_create_struct { - ($e1:expr) => { - Box::new(move |state| create_struct(state, $e1, None)) - }; -} - -#[macro_export] -macro_rules! state_create_struct_from_inst { - ($e1:expr) => { - Box::new(move |state| create_struct_from_inst(state, $e1)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_has_struct` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_has_struct { - ($e:expr) => { - Box::new(move |state| stack_has_struct(state, $e)) - }; -} - -#[macro_export] -macro_rules! state_stack_has_struct_inst { - ($e:expr) => { - Box::new(move |state| stack_has_struct_inst(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_unpack_struct` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_unpack_struct { - ($e:expr) => { - Box::new(move |state| stack_unpack_struct(state, $e, None)) - }; -} - -#[macro_export] -macro_rules! state_stack_unpack_struct_inst { - ($e:expr) => { - Box::new(move |state| stack_unpack_struct_inst(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `struct_abilities` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_struct_has_key { - ($e:expr) => { - Box::new(move |state| { - struct_abilities( - state, - $e, - &move_binary_format::file_format::Signature(vec![]), - ) - .has_key() - }) - }; -} - -#[macro_export] -macro_rules! state_struct_inst_has_key { - ($e:expr) => { - Box::new(move |state| struct_inst_abilities(state, $e).has_key()) - }; -} - -/// Wrapper for enclosing the arguments of `struct_has_field` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_struct_has_field { - ($e:expr) => { - Box::new(move |state| stack_struct_has_field(state, $e)) - }; -} - -#[macro_export] -macro_rules! state_stack_struct_has_field_inst { - ($e:expr) => { - Box::new(move |state| stack_struct_has_field_inst(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_struct_borrow_field` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_struct_borrow_field { - ($e:expr) => { - Box::new(move |state| stack_struct_borrow_field(state, $e)) - }; -} - -#[macro_export] -macro_rules! state_stack_struct_borrow_field_inst { - ($e:expr) => { - Box::new(move |state| stack_struct_borrow_field_inst(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_has_reference` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_has_reference { - ($e1:expr, $e2:expr) => { - Box::new(move |state| stack_has_reference(state, $e1, $e2)) - }; -} - -/// Wrapper for enclosing the arguments of `register_dereference` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_register_dereference { - () => { - Box::new(move |state| register_dereference(state)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_push_register_borrow` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_push_register_borrow { - ($e:expr) => { - Box::new(move |state| stack_push_register_borrow(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_satisfies_function_signature` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_satisfies_function_signature { - ($e:expr) => { - Box::new(move |state| stack_satisfies_function_signature(state, $e).0) - }; -} - -#[macro_export] -macro_rules! state_stack_satisfies_function_inst_signature { - ($e:expr) => { - Box::new(move |state| stack_satisfies_function_inst_signature(state, $e).0) - }; -} - -/// Wrapper for enclosing the arguments of `stack_function_popn` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_function_popn { - ($e:expr) => { - Box::new(move |state| stack_function_popn(state, $e)) - }; -} - -#[macro_export] -macro_rules! state_stack_function_inst_popn { - ($e:expr) => { - Box::new(move |state| stack_function_inst_popn(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `stack_function_call` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_stack_function_call { - ($e:expr) => { - Box::new(move |state| stack_function_call(state, $e, None)) - }; -} - -#[macro_export] -macro_rules! state_stack_function_inst_call { - ($e:expr) => { - Box::new(move |state| stack_function_inst_call(state, $e)) - }; -} - -/// Determine the proper type instantiation for function call in the current state. -#[macro_export] -macro_rules! function_instantiation_for_state { - ($e:expr) => { - Box::new(move |state| get_function_instantiation_for_state(state, $e)) - }; -} - -/// Wrapper for enclosing the arguments of `function_can_acquire_resource` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_function_can_acquire_resource { - () => { - Box::new(move |state| function_can_acquire_resource(state)) - }; -} - -/// Wrapper for enclosing the arguments of `memory_safe` so that only the -/// `state` needs to be given. -#[macro_export] -macro_rules! state_memory_safe { - ($e:expr) => { - Box::new(move |state| memory_safe(state, $e)) - }; -} - -/// Predicate that is false for every state. -#[macro_export] -macro_rules! state_never { - () => { - Box::new(|_| (false)) - }; -} - -#[macro_export] -macro_rules! state_stack_bin_op { - (#left) => { - Box::new(move |state| stack_bin_op(state, $crate::transitions::StackBinOpResult::Left)) - }; - (#right) => { - Box::new(move |state| stack_bin_op(state, $crate::transitions::StackBinOpResult::Right)) - }; - () => { - state_stack_bin_op!(#left) - }; - ($e: expr) => { - Box::new(move |state| stack_bin_op(state, $crate::transitions::StackBinOpResult::Other($e))) - } -} - -/// Predicate that is false for every state, unless control operations are allowed. -#[macro_export] -macro_rules! state_control_flow { - () => { - Box::new(|state| state.is_control_flow_allowed()) - }; -} - -/// Determine the proper type instantiation for struct in the current state. -#[macro_export] -macro_rules! struct_instantiation_for_state { - ($e:expr, $is_exact:expr) => { - Box::new(move |state| get_struct_instantiation_for_state(state, $e, $is_exact)) - }; -} - -/// Determine the proper type instantiation for struct in the current state. -#[macro_export] -macro_rules! unpack_instantiation_for_state { - () => { - Box::new(move |state| stack_unpack_struct_instantiation(state)) - }; -} - -/// A wrapper around type instantiation, that allows specifying an "exact" instantiation index, or -/// if the instantiation should be inferred from the current state. -#[macro_export] -macro_rules! with_ty_param { - (($is_exact:expr, $struct_inst_idx:expr) => $s_inst_idx:ident, $body:expr) => { - Box::new(move |$s_inst_idx| { - let $s_inst_idx = if $is_exact { - $struct_inst_idx - } else { - $s_inst_idx - }; - $body - }) - }; -} diff --git a/third_party/move/testing-infra/test-generation/tests/boolean_instructions.rs b/third_party/move/testing-infra/test-generation/tests/boolean_instructions.rs deleted file mode 100644 index 573ae4e1b91f1f..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/boolean_instructions.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -#[test] -fn bytecode_and() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::And, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_or() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Or, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_not() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Not, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} diff --git a/third_party/move/testing-infra/test-generation/tests/call_graph.rs b/third_party/move/testing-infra/test-generation/tests/call_graph.rs deleted file mode 100644 index 98917610803f6a..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/call_graph.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::FunctionHandleIndex; -use test_generation::abstract_state::CallGraph; - -#[test] -fn call_graph_no_self_call_recursive() { - let call_graph = CallGraph::new(10); - let can_call = call_graph.can_call(FunctionHandleIndex(0)); - assert!(can_call.len() == 9); - assert!(!can_call.iter().any(|fh| *fh == FunctionHandleIndex(0))); -} - -#[test] -fn call_graph_simple_recursive_call() { - let mut call_graph = CallGraph::new(10); - call_graph.add_call(FunctionHandleIndex(0), FunctionHandleIndex(1)); - let can_call0 = call_graph.can_call(FunctionHandleIndex(0)); - let can_call1 = call_graph.can_call(FunctionHandleIndex(1)); - assert!(can_call0.len() == 9); - assert!(can_call1.len() == 8); - assert!(!can_call0.iter().any(|fh| *fh == FunctionHandleIndex(0))); - assert!(!can_call1 - .iter() - .any(|fh| *fh == FunctionHandleIndex(0) || *fh == FunctionHandleIndex(1))); -} - -#[test] -fn call_graph_transitive_recursive_call() { - let mut call_graph = CallGraph::new(10); - call_graph.add_call(FunctionHandleIndex(1), FunctionHandleIndex(2)); - call_graph.add_call(FunctionHandleIndex(2), FunctionHandleIndex(0)); - let can_call0 = call_graph.can_call(FunctionHandleIndex(0)); - assert!(can_call0.len() == 7); -} - -#[test] -fn call_graph_call_graph_depth() { - let mut call_graph = CallGraph::new(10); - call_graph.add_call(FunctionHandleIndex(0), FunctionHandleIndex(1)); - call_graph.add_call(FunctionHandleIndex(1), FunctionHandleIndex(2)); - call_graph.add_call(FunctionHandleIndex(1), FunctionHandleIndex(3)); - call_graph.add_call(FunctionHandleIndex(3), FunctionHandleIndex(2)); - assert!( - call_graph - .call_depth(FunctionHandleIndex(0), FunctionHandleIndex(1)) - .unwrap() - == 3 - ); -} - -#[test] -fn call_graph_call_call_into_graph_depth() { - let mut call_graph = CallGraph::new(10); - call_graph.add_call(FunctionHandleIndex(0), FunctionHandleIndex(1)); - call_graph.add_call(FunctionHandleIndex(1), FunctionHandleIndex(2)); - call_graph.add_call(FunctionHandleIndex(1), FunctionHandleIndex(3)); - call_graph.add_call(FunctionHandleIndex(3), FunctionHandleIndex(2)); - assert!(call_graph.max_calling_depth(FunctionHandleIndex(2)) == 3); -} diff --git a/third_party/move/testing-infra/test-generation/tests/common.rs b/third_party/move/testing-infra/test-generation/tests/common.rs deleted file mode 100644 index 732aa34767021e..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/common.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{Bytecode, FunctionInstantiation, StructDefInstantiation}; -use test_generation::{ - abstract_state::AbstractState, - config::ALLOW_MEMORY_UNSAFE, - summaries::{instruction_summary, Effects}, -}; - -pub fn run_instruction( - instruction: Bytecode, - mut initial_state: AbstractState, -) -> (AbstractState, Bytecode) { - let summary = instruction_summary(instruction.clone(), false); - // The following is temporary code. If ALLOW_MEMORY_UNSAFE is false, - // we have to ignore that precondition so that we can test the instructions. - // When memory safety is implemented, only the false branch should be used. - let unsatisfied_preconditions = if !ALLOW_MEMORY_UNSAFE { - match instruction { - Bytecode::Pop - //| Bytecode::MoveLoc(_) - | Bytecode::CopyLoc(_) - | Bytecode::MutBorrowLoc(_) - | Bytecode::ImmBorrowLoc(_) - | Bytecode::StLoc(_) - | Bytecode::ReadRef - | Bytecode::WriteRef - | Bytecode::FreezeRef - | Bytecode::MutBorrowField(_) - | Bytecode::MutBorrowFieldGeneric(_) - | Bytecode::ImmBorrowFieldGeneric(_) - | Bytecode::ImmBorrowField(_) - | Bytecode::MutBorrowGlobal(_) - | Bytecode::MutBorrowGlobalGeneric(_) - | Bytecode::ImmBorrowGlobal(_) - | Bytecode::ImmBorrowGlobalGeneric(_) - | Bytecode::MoveTo(_) - | Bytecode::MoveToGeneric(_) => { - let len = summary.preconditions.len(); - summary.preconditions[..(len - 1)] - .iter() - .any(|precondition| !precondition(&initial_state)) - } - Bytecode::Eq | Bytecode::Neq => { - let len = summary.preconditions.len(); - summary.preconditions[..(len - 2)] - .iter() - .any(|precondition| !precondition(&initial_state)) - } - _ => summary - .preconditions - .iter() - .any(|precondition| !precondition(&initial_state)), - } - } else { - summary - .preconditions - .iter() - .any(|precondition| !precondition(&initial_state)) - }; - assert!( - !unsatisfied_preconditions, - "preconditions of instruction not satisfied" - ); - match summary.effects { - Effects::TyParams(instantiation, effect, instantiation_application) => { - let (struct_idx, instantiation) = instantiation(&initial_state); - let index = initial_state.module.add_instantiation(instantiation); - let struct_inst = StructDefInstantiation { - def: struct_idx, - type_parameters: index, - }; - let str_inst_idx = initial_state.module.add_struct_instantiation(struct_inst); - let effects = effect(str_inst_idx); - let instruction = instantiation_application(str_inst_idx); - ( - effects.iter().fold(initial_state, |acc, effect| { - effect(&acc) - .unwrap_or_else(|err| panic!("Error applying instruction effect: {}", err)) - }), - instruction, - ) - }, - Effects::TyParamsCall(instantiation, effect, instantiation_application) => { - let (fh_idx, instantiation) = instantiation(&initial_state); - let index = initial_state.module.add_instantiation(instantiation); - let func_inst = FunctionInstantiation { - handle: fh_idx, - type_parameters: index, - }; - let func_inst_idx = initial_state.module.add_function_instantiation(func_inst); - let effects = effect(func_inst_idx); - let instruction = instantiation_application(func_inst_idx); - ( - effects.iter().fold(initial_state, |acc, effect| { - effect(&acc) - .unwrap_or_else(|err| panic!("Error applying instruction effect: {}", err)) - }), - instruction, - ) - }, - Effects::NoTyParams(effects) => ( - effects.iter().fold(initial_state, |acc, effect| { - effect(&acc) - .unwrap_or_else(|err| panic!("Error applying instruction effect: {}", err)) - }), - instruction, - ), - } -} diff --git a/third_party/move/testing-infra/test-generation/tests/comparison_instructions.rs b/third_party/move/testing-infra/test-generation/tests/comparison_instructions.rs deleted file mode 100644 index fe32abb2d5e6ec..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/comparison_instructions.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use itertools::Itertools; -use move_binary_format::file_format::{Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -const INTEGER_TYPES: &[SignatureToken] = &[ - SignatureToken::U8, - SignatureToken::U16, - SignatureToken::U32, - SignatureToken::U64, - SignatureToken::U128, - SignatureToken::U256, -]; - -#[test] -fn bytecode_comparison_integers() { - for (op, ty) in [ - Bytecode::Lt, - Bytecode::Gt, - Bytecode::Le, - Bytecode::Ge, - Bytecode::Eq, - Bytecode::Neq, - ] - .iter() - .cartesian_product(INTEGER_TYPES.iter()) - { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(ty.clone())); - state1.stack_push(AbstractValue::new_primitive(ty.clone())); - let (state2, _) = common::run_instruction(op.clone(), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); - } -} - -#[test] -fn bytecode_eq_bool() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Eq, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_neq_u64() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - let (state2, _) = common::run_instruction(Bytecode::Neq, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_neq_bool() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Neq, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} diff --git a/third_party/move/testing-infra/test-generation/tests/control_flow_instructions.rs b/third_party/move/testing-infra/test-generation/tests/control_flow_instructions.rs deleted file mode 100644 index 9073b1f5db386f..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/control_flow_instructions.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{ - empty_module, Bytecode, CompiledModule, FunctionHandle, FunctionHandleIndex, IdentifierIndex, - ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, -}; -use move_core_types::identifier::Identifier; -use std::collections::HashMap; -use test_generation::abstract_state::{AbstractState, AbstractValue, CallGraph}; - -mod common; - -fn generate_module_with_function() -> CompiledModule { - let mut module = empty_module(); - - let offset = module.identifiers.len(); - module.identifiers.push(Identifier::new("func0").unwrap()); - - module.signatures = vec![ - Signature(vec![]), - Signature(vec![SignatureToken::U64, SignatureToken::Bool]), - Signature(vec![SignatureToken::Address]), - ]; - - module.function_handles = vec![FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(offset as u16), - parameters: SignatureIndex::new(1), - return_: SignatureIndex::new(2), - type_parameters: vec![], - access_specifiers: None, - }]; - module -} - -#[test] -fn bytecode_call() { - let module = generate_module_with_function(); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Address)), - "stack type postcondition not satisfied", - ); -} - -#[test] -#[should_panic] -fn bytecode_call_function_signature_not_satisfied() { - let module = generate_module_with_function(); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); -} - -#[test] -#[should_panic] -fn bytecode_call_return_not_pushed() { - let module = generate_module_with_function(); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - let (state2, _) = common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); - assert_eq!(state2.stack_len(), 0,); -} diff --git a/third_party/move/testing-infra/test-generation/tests/generic_instructions.rs b/third_party/move/testing-infra/test-generation/tests/generic_instructions.rs deleted file mode 100644 index 0f6305f419638c..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/generic_instructions.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::SignatureToken; -use test_generation::transitions::Subst; - -//--------------------------------------------------------------------------- -// Substitution tests -//--------------------------------------------------------------------------- - -#[test] -fn unify_no_subst() { - use SignatureToken::*; - let tys = [Bool, U64, Vector(Box::new(U8)), Address]; - for tok1 in tys.iter() { - for tok2 in tys.iter() { - let should_unify = tok1.clone() == tok2.clone(); - let mut s = Subst::new(); - assert!(s.check_and_add(tok1.clone(), tok2.clone()) == should_unify); - assert!(s.instantiation().is_empty()); - } - } -} - -#[test] -fn unify_ty_param_empty_subst1() { - use SignatureToken::*; - let mut subst = Subst::new(); - assert!(subst.check_and_add(Bool, TypeParameter(0))); - assert!(!subst.check_and_add(U64, TypeParameter(0))); - assert!(!subst.check_and_add(TypeParameter(0), U64)); - assert!(!subst.check_and_add(TypeParameter(1), U64)); - assert!(subst.check_and_add(U64, TypeParameter(1))); - // Even if a type parameter can map to an instantiantion (due to a ground type on the stack) a - // non-grounded type on the stack cannot be unified with a particular instantiation. - assert!(!subst.check_and_add(TypeParameter(1), U64)); - assert!(subst.instantiation().len() == 2); -} - -#[test] -fn unify_ty_param_empty_subst2() { - use SignatureToken::*; - let mut subst = Subst::new(); - assert!(subst.check_and_add(U64, TypeParameter(0))); - assert!(subst.check_and_add(U64, TypeParameter(1))); - assert!(subst.check_and_add(Bool, TypeParameter(2))); - - assert!(!subst.check_and_add(TypeParameter(0), U64)); - assert!(!subst.check_and_add(TypeParameter(1), U64)); - assert!(!subst.check_and_add(TypeParameter(2), Bool)); - - assert!(subst.check_and_add(U64, TypeParameter(0))); - assert!(subst.check_and_add(U64, TypeParameter(1))); - assert!(subst.check_and_add(Bool, TypeParameter(2))); - - assert!(!subst.check_and_add(TypeParameter(0), TypeParameter(1))); - - assert!(!subst.check_and_add(TypeParameter(0), TypeParameter(2))); - assert!(!subst.check_and_add(TypeParameter(1), TypeParameter(2))); - - assert!(!subst.check_and_add(TypeParameter(2), TypeParameter(0))); - assert!(!subst.check_and_add(TypeParameter(2), TypeParameter(1))); - assert!(subst.instantiation().len() == 3); -} - -#[test] -fn unify_ty_params_infinite() { - use SignatureToken::*; - let mut subst = Subst::new(); - assert!(subst.check_and_add(TypeParameter(0), TypeParameter(1))); - assert!(subst.check_and_add(TypeParameter(1), TypeParameter(0))); - // These should both return false. - assert!(!subst.check_and_add(Bool, TypeParameter(0))); - assert!(!subst.check_and_add(TypeParameter(0), Bool)); -} - -#[test] -fn unify_ty_param_empty_subst3() { - use SignatureToken::*; - let mut subst = Subst::new(); - assert!(subst.check_and_add(TypeParameter(1), TypeParameter(0))); - assert!(subst.instantiation().len() == 1); -} - -#[test] -fn unify_ty_param_empty_subst4() { - use SignatureToken::*; - let mut subst = Subst::new(); - assert!(subst.check_and_add(Bool, TypeParameter(0))); - assert!(!subst.check_and_add(U64, TypeParameter(0))); - assert!(subst.check_and_add(U64, TypeParameter(1))); - assert!(subst.check_and_add(U64, TypeParameter(2))); -} diff --git a/third_party/move/testing-infra/test-generation/tests/integer_instructions.rs b/third_party/move/testing-infra/test-generation/tests/integer_instructions.rs deleted file mode 100644 index 0e4bda13fee95c..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/integer_instructions.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use itertools::Itertools; -use move_binary_format::file_format::{Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -const INTEGER_TYPES: &[SignatureToken] = &[ - SignatureToken::U8, - SignatureToken::U16, - SignatureToken::U32, - SignatureToken::U64, - SignatureToken::U128, - SignatureToken::U256, -]; - -#[test] -fn bytecode_bin_ops() { - for (op, ty) in [ - Bytecode::Add, - Bytecode::Sub, - Bytecode::Mul, - Bytecode::Div, - Bytecode::Mod, - Bytecode::BitAnd, - Bytecode::BitOr, - Bytecode::Xor, - ] - .iter() - .cartesian_product(INTEGER_TYPES.iter()) - { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(ty.clone())); - state1.stack_push(AbstractValue::new_primitive(ty.clone())); - let (state2, _) = common::run_instruction(op.clone(), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(ty.clone())), - "stack type postcondition not met" - ); - } -} - -#[test] -fn bytecode_shl_shr() { - for (op, ty) in [Bytecode::Shl, Bytecode::Shr] - .iter() - .cartesian_product(INTEGER_TYPES.iter()) - { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(ty.clone())); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U8)); - let (state2, _) = common::run_instruction(op.clone(), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(ty.clone())), - "stack type postcondition not met" - ); - } -} - -#[test] -fn bytecode_casting_ops() { - for (op, ty1, ty2) in [ - (Bytecode::CastU8, SignatureToken::U8, SignatureToken::U8), - (Bytecode::CastU16, SignatureToken::U16, SignatureToken::U16), - (Bytecode::CastU16, SignatureToken::U16, SignatureToken::U8), - (Bytecode::CastU32, SignatureToken::U32, SignatureToken::U32), - (Bytecode::CastU32, SignatureToken::U32, SignatureToken::U8), - (Bytecode::CastU32, SignatureToken::U32, SignatureToken::U16), - (Bytecode::CastU64, SignatureToken::U64, SignatureToken::U8), - (Bytecode::CastU64, SignatureToken::U64, SignatureToken::U16), - (Bytecode::CastU64, SignatureToken::U64, SignatureToken::U32), - (Bytecode::CastU64, SignatureToken::U64, SignatureToken::U64), - (Bytecode::CastU128, SignatureToken::U128, SignatureToken::U8), - ( - Bytecode::CastU128, - SignatureToken::U128, - SignatureToken::U16, - ), - ( - Bytecode::CastU128, - SignatureToken::U128, - SignatureToken::U32, - ), - ( - Bytecode::CastU128, - SignatureToken::U128, - SignatureToken::U64, - ), - ( - Bytecode::CastU128, - SignatureToken::U128, - SignatureToken::U128, - ), - (Bytecode::CastU256, SignatureToken::U256, SignatureToken::U8), - ( - Bytecode::CastU256, - SignatureToken::U256, - SignatureToken::U16, - ), - ( - Bytecode::CastU256, - SignatureToken::U256, - SignatureToken::U32, - ), - ( - Bytecode::CastU256, - SignatureToken::U256, - SignatureToken::U64, - ), - ( - Bytecode::CastU256, - SignatureToken::U256, - SignatureToken::U128, - ), - ( - Bytecode::CastU256, - SignatureToken::U256, - SignatureToken::U256, - ), - ] - .iter() - { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(ty2.clone())); - let (state2, _) = common::run_instruction(op.clone(), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(ty1.clone())), - "stack type postcondition not met" - ); - } -} diff --git a/third_party/move/testing-infra/test-generation/tests/load_instructions.rs b/third_party/move/testing-infra/test-generation/tests/load_instructions.rs deleted file mode 100644 index 5409ce26c9b772..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/load_instructions.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{Bytecode, ConstantPoolIndex, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -#[test] -fn bytecode_ldu64() { - let state1 = AbstractState::new(); - let (state2, _) = common::run_instruction(Bytecode::LdU64(0), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::U64)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_ldtrue() { - let state1 = AbstractState::new(); - let (state2, _) = common::run_instruction(Bytecode::LdTrue, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_ldfalse() { - let state1 = AbstractState::new(); - let (state2, _) = common::run_instruction(Bytecode::LdFalse, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_ldconst() { - let state1 = AbstractState::new(); - let (state2, _) = common::run_instruction(Bytecode::LdConst(ConstantPoolIndex::new(0)), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Address)), - "stack type postcondition not met" - ); -} diff --git a/third_party/move/testing-infra/test-generation/tests/local_instructions.rs b/third_party/move/testing-infra/test-generation/tests/local_instructions.rs deleted file mode 100644 index 9cb0174efb4360..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/local_instructions.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{AbilitySet, Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue, BorrowState}; - -mod common; - -#[test] -fn bytecode_copyloc() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available, - ); - let (state2, _) = common::run_instruction(Bytecode::CopyLoc(0), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::U64)), - "stack type postcondition not met" - ); - assert_eq!( - state2.local_get(0), - Some(&( - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available - )), - "locals signature postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_copyloc_no_local() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::CopyLoc(0), state1); -} - -#[test] -#[should_panic] -fn bytecode_copyloc_local_unavailable() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Unavailable, - ); - common::run_instruction(Bytecode::CopyLoc(0), state1); -} - -#[test] -fn bytecode_moveloc() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available, - ); - let (state2, _) = common::run_instruction(Bytecode::MoveLoc(0), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::U64)), - "stack type postcondition not met" - ); - assert_eq!( - state2.local_get(0), - Some(&( - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Unavailable - )), - "locals signature postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_moveloc_no_local() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::MoveLoc(0), state1); -} - -#[test] -#[should_panic] -fn bytecode_moveloc_local_unavailable() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Unavailable, - ); - common::run_instruction(Bytecode::MoveLoc(0), state1); -} - -#[test] -fn bytecode_mutborrowloc() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available, - ); - let (state2, _) = common::run_instruction(Bytecode::MutBorrowLoc(0), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES - )), - "stack type postcondition not met" - ); - assert_eq!( - state2.local_get(0), - Some(&( - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available - )), - "locals signature postcondition not met" - ); -} - -#[test] -fn bytecode_immborrowloc() { - let mut state1 = AbstractState::new(); - state1.local_insert( - 0, - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available, - ); - let (state2, _) = common::run_instruction(Bytecode::ImmBorrowLoc(0), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::U64),), - AbilitySet::PRIMITIVES - )), - "stack type postcondition not met" - ); - assert_eq!( - state2.local_get(0), - Some(&( - AbstractValue::new_primitive(SignatureToken::U64), - BorrowState::Available - )), - "locals signature postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_mutborrowloc_no_local() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::MutBorrowLoc(0), state1); -} - -#[test] -#[should_panic] -fn bytecode_immborrowloc_no_local() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::ImmBorrowLoc(0), state1); -} - -// TODO: Turn back on when borrow graph and references are allowed -// #[test] -// #[should_panic] -// fn bytecode_mutborrowloc_local_unavailable() { -// let mut state1 = AbstractState::new(); -// state1.local_insert( -// 0, -// AbstractValue::new_primitive(SignatureToken::U64), -// BorrowState::Unavailable, -// ); -// common::run_instruction(Bytecode::MutBorrowLoc(0), state1); -// } -// -// #[test] -// #[should_panic] -// fn bytecode_immborrowloc_local_unavailable() { -// let mut state1 = AbstractState::new(); -// state1.local_insert( -// 0, -// AbstractValue::new_primitive(SignatureToken::U64), -// BorrowState::Unavailable, -// ); -// common::run_instruction(Bytecode::ImmBorrowLoc(0), state1); -// } diff --git a/third_party/move/testing-infra/test-generation/tests/reference_instructions.rs b/third_party/move/testing-infra/test-generation/tests/reference_instructions.rs deleted file mode 100644 index 5a62db777d3035..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/reference_instructions.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{AbilitySet, Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -#[test] -fn bytecode_readref() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - let (state2, _) = common::run_instruction(Bytecode::ReadRef, state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::U64)), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_readref_no_ref() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::ReadRef, state1); -} - -#[test] -#[should_panic] -fn bytecode_readref_wrong_dereference() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - let (state2, _) = common::run_instruction(Bytecode::ReadRef, state1); - assert!( - state2.stack_peek(0) != Some(AbstractValue::new_primitive(SignatureToken::U64)), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_writeref() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - let (state2, _) = common::run_instruction(Bytecode::WriteRef, state1); - assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); -} - -#[test] -#[should_panic] -fn bytecode_writeref_type_mismatch() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - common::run_instruction(Bytecode::WriteRef, state1); -} - -#[test] -#[should_panic] -fn bytecode_writeref_stack_len_mismatch() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - let (state2, _) = common::run_instruction(Bytecode::WriteRef, state1); - assert!(state2.stack_len() != 0, "stack type postcondition not met"); -} - -#[test] -fn bytecode_feezeref() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - let (state2, _) = common::run_instruction(Bytecode::FreezeRef, state1); - assert_eq!(state2.stack_len(), 1, "stack len postcondition not met"); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES - )), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_feezeref_no_ref() { - let state1 = AbstractState::new(); - common::run_instruction(Bytecode::FreezeRef, state1); -} - -#[test] -#[should_panic] -fn bytecode_feezeref_already_immutable() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::U64)), - AbilitySet::PRIMITIVES, - )); - common::run_instruction(Bytecode::FreezeRef, state1); -} diff --git a/third_party/move/testing-infra/test-generation/tests/special_instructions.rs b/third_party/move/testing-infra/test-generation/tests/special_instructions.rs deleted file mode 100644 index f409f0d2bb1efa..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/special_instructions.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::file_format::{Bytecode, SignatureToken}; -use test_generation::abstract_state::{AbstractState, AbstractValue}; - -mod common; - -#[test] -fn bytecode_pop() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); - let (state2, _) = common::run_instruction(Bytecode::Pop, state1); - assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); -} - -#[test] -fn bytecode_createaccount() { - let mut state1 = AbstractState::new(); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - let (state2, _) = common::run_instruction(Bytecode::Pop, state1); - assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); -} diff --git a/third_party/move/testing-infra/test-generation/tests/struct_instructions.rs b/third_party/move/testing-infra/test-generation/tests/struct_instructions.rs deleted file mode 100644 index a213bc0c3fdb78..00000000000000 --- a/third_party/move/testing-infra/test-generation/tests/struct_instructions.rs +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -extern crate test_generation; -use move_binary_format::{ - access::ModuleAccess, - file_format::{ - empty_module, Ability, AbilitySet, Bytecode, CompiledModule, FieldDefinition, FieldHandle, - FieldHandleIndex, IdentifierIndex, ModuleHandleIndex, SignatureToken, StructDefinition, - StructDefinitionIndex, StructFieldInformation, StructHandle, StructHandleIndex, TableIndex, - TypeSignature, - }, - views::{StructDefinitionView, ViewInternals}, -}; -use move_core_types::identifier::Identifier; -use std::collections::HashMap; -use test_generation::{ - abilities, - abstract_state::{AbstractState, AbstractValue, CallGraph}, -}; - -mod common; - -fn generate_module_with_struct(resource: bool) -> CompiledModule { - let mut module: CompiledModule = empty_module(); - - let struct_index = 0; - let num_fields = 5; - let offset = module.identifiers.len() as TableIndex; - module.identifiers.push(Identifier::new("struct0").unwrap()); - - let mut fields = vec![]; - for i in 0..num_fields { - module - .identifiers - .push(Identifier::new(format!("string{}", i)).unwrap()); - let str_pool_idx = IdentifierIndex::new(i + 1); - fields.push(FieldDefinition { - name: str_pool_idx, - signature: TypeSignature(SignatureToken::Bool), - }); - } - let struct_def = StructDefinition { - struct_handle: StructHandleIndex(struct_index), - field_information: StructFieldInformation::Declared(fields), - }; - module.struct_defs.push(struct_def); - module.struct_handles = vec![StructHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new((struct_index + offset) as TableIndex), - abilities: if resource { - AbilitySet::EMPTY | Ability::Key | Ability::Store - } else { - AbilitySet::PRIMITIVES - }, - type_parameters: vec![], - }]; - module -} - -fn create_struct_value(module: &CompiledModule) -> (AbstractValue, Vec) { - let struct_def = module.struct_def_at(StructDefinitionIndex::new(0)); - let struct_def_view = StructDefinitionView::new(module, struct_def); - let tokens: Vec = struct_def_view - .fields() - .into_iter() - .flatten() - .map(|field| field.type_signature().token().as_inner().clone()) - .collect(); - let struct_abilities = struct_def_view.abilities(); - - let type_argument_abilities = tokens.iter().map(|arg| abilities(module, arg, &[])); - let declared_phantom_parameters = [false].repeat(type_argument_abilities.len()); - let abilities = AbilitySet::polymorphic_abilities( - struct_abilities, - declared_phantom_parameters, - type_argument_abilities, - ) - .unwrap(); - ( - AbstractValue::new_struct(SignatureToken::Struct(struct_def.struct_handle), abilities), - tokens, - ) -} - -fn get_field_signature<'a>(module: &'a CompiledModule, handle: &FieldHandle) -> &'a SignatureToken { - let struct_def = &module.struct_defs[handle.owner.0 as usize]; - match &struct_def.field_information { - StructFieldInformation::Native => panic!("borrow field on a native struct"), - StructFieldInformation::Declared(fields) => &fields[handle.field as usize].signature.0, - StructFieldInformation::DeclaredVariants(..) => { - // TODO(#13806): consider implementing for struct variants - panic!("enum types not yet implemented") - }, - } -} - -#[test] -#[should_panic] -fn bytecode_pack_signature_not_satisfied() { - let module = generate_module_with_struct(false); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::Pack(StructDefinitionIndex::new(0)), state1); -} - -#[test] -fn bytecode_pack() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let (struct_value1, tokens) = create_struct_value(&state1.module.module); - for token in tokens { - let abstract_value = AbstractValue { - token: token.clone(), - abilities: abilities(&state1.module.module, &token, &[]), - }; - state1.stack_push(abstract_value); - } - let (state2, _) = - common::run_instruction(Bytecode::Pack(StructDefinitionIndex::new(0)), state1); - let struct_value2 = state2.stack_peek(0).expect("struct not added to stack"); - assert_eq!( - struct_value1, struct_value2, - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_unpack_signature_not_satisfied() { - let module = generate_module_with_struct(false); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::Unpack(StructDefinitionIndex::new(0)), state1); -} - -#[test] -fn bytecode_unpack() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let (struct_value, tokens) = create_struct_value(&state1.module.module); - state1.stack_push(struct_value); - let (state2, _) = - common::run_instruction(Bytecode::Unpack(StructDefinitionIndex::new(0)), state1); - assert_eq!( - state2.stack_len(), - tokens.len(), - "stack type postcondition not met" - ); -} - -#[test] -fn bytecode_exists() { - let module = generate_module_with_struct(true); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - let (state2, _) = - common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue::new_primitive(SignatureToken::Bool)), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_exists_struct_is_not_resource() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); -} - -#[test] -#[should_panic] -fn bytecode_exists_no_address_on_stack() { - let module = generate_module_with_struct(true); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); -} - -#[test] -fn bytecode_movefrom() { - let module = generate_module_with_struct(true); - let mut state1 = AbstractState::from_locals( - module, - HashMap::new(), - vec![], - vec![StructDefinitionIndex::new(0)], - CallGraph::new(0), - ); - let state1_copy = state1.clone(); - let struct_def = state1_copy - .module - .module - .struct_def_at(StructDefinitionIndex::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - let (state2, _) = - common::run_instruction(Bytecode::MoveFrom(StructDefinitionIndex::new(0)), state1); - let struct_value = state2.stack_peek(0).expect("struct not added to stack"); - assert!( - matches!(struct_value.token, SignatureToken::Struct(struct_handle) if struct_handle == struct_def.struct_handle), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_movefrom_struct_is_not_resource() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - common::run_instruction(Bytecode::MoveFrom(StructDefinitionIndex::new(0)), state1); -} - -#[test] -#[should_panic] -fn bytecode_movefrom_no_address_on_stack() { - let module = generate_module_with_struct(true); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::MoveFrom(StructDefinitionIndex::new(0)), state1); -} - -#[test] -fn bytecode_moveto() { - let module = generate_module_with_struct(true); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::Signer)), - AbilitySet::EMPTY | Ability::Drop, - )); - state1.stack_push(create_struct_value(&state1.module.module).0); - let (state2, _) = - common::run_instruction(Bytecode::MoveTo(StructDefinitionIndex::new(0)), state1); - assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); -} - -#[test] -#[should_panic] -fn bytecode_moveto_struct_is_not_resource() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::Signer)), - AbilitySet::EMPTY | Ability::Drop, - )); - state1.stack_push(create_struct_value(&state1.module.module).0); - common::run_instruction(Bytecode::MoveTo(StructDefinitionIndex::new(0)), state1); -} - -#[test] -#[should_panic] -fn bytecode_moveto_no_struct_on_stack() { - let module = generate_module_with_struct(true); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_reference( - SignatureToken::Reference(Box::new(SignatureToken::Signer)), - AbilitySet::EMPTY | Ability::Drop, - )); - common::run_instruction(Bytecode::MoveTo(StructDefinitionIndex::new(0)), state1); -} - -#[test] -fn bytecode_mutborrowfield() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - let field_signature = - get_field_signature(&module, &module.field_handles[field_handle_idx.0 as usize]).clone(); - - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let struct_value = create_struct_value(&state1.module.module).0; - state1.stack_push(AbstractValue { - token: SignatureToken::MutableReference(Box::new(struct_value.token)), - abilities: struct_value.abilities, - }); - let (state2, _) = common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); - let abilities = abilities(&state2.module.module, &field_signature, &[]); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue { - token: SignatureToken::MutableReference(Box::new(field_signature)), - abilities, - }), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_mutborrowfield_stack_has_no_reference() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); -} - -#[test] -#[should_panic] -fn bytecode_mutborrowfield_ref_is_immutable() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let struct_value = create_struct_value(&state1.module.module).0; - state1.stack_push(AbstractValue { - token: SignatureToken::Reference(Box::new(struct_value.token)), - abilities: struct_value.abilities, - }); - common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); -} - -#[test] -fn bytecode_immborrowfield() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - let field_signature = - get_field_signature(&module, &module.field_handles[field_handle_idx.0 as usize]).clone(); - - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let struct_value = create_struct_value(&state1.module.module).0; - state1.stack_push(AbstractValue { - token: SignatureToken::Reference(Box::new(struct_value.token)), - abilities: struct_value.abilities, - }); - let (state2, _) = common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); - let abilities = abilities(&state2.module.module, &field_signature, &[]); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue { - token: SignatureToken::MutableReference(Box::new(field_signature)), - abilities, - }), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_immborrowfield_stack_has_no_reference() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); -} - -#[test] -#[should_panic] -fn bytecode_immborrowfield_ref_is_mutable() { - let mut module: CompiledModule = generate_module_with_struct(false); - let struct_def_idx = StructDefinitionIndex((module.struct_defs.len() - 1) as u16); - module.field_handles.push(FieldHandle { - owner: struct_def_idx, - field: 0, - }); - let field_handle_idx = FieldHandleIndex((module.field_handles.len() - 1) as u16); - - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let struct_value = create_struct_value(&state1.module.module).0; - state1.stack_push(AbstractValue { - token: SignatureToken::MutableReference(Box::new(struct_value.token)), - abilities: struct_value.abilities, - }); - common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); -} - -#[test] -fn bytecode_borrowglobal() { - let module = generate_module_with_struct(true); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - let struct_value = create_struct_value(&state1.module.module).0; - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - let (state2, _) = common::run_instruction( - Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), - state1, - ); - assert_eq!( - state2.stack_peek(0), - Some(AbstractValue { - token: SignatureToken::MutableReference(Box::new(struct_value.token)), - abilities: struct_value.abilities, - }), - "stack type postcondition not met" - ); -} - -#[test] -#[should_panic] -fn bytecode_borrowglobal_struct_is_not_resource() { - let module = generate_module_with_struct(false); - let mut state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - state1.stack_push(AbstractValue::new_primitive(SignatureToken::Address)); - common::run_instruction( - Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), - state1, - ); -} - -#[test] -#[should_panic] -fn bytecode_borrowglobal_no_address_on_stack() { - let module = generate_module_with_struct(true); - let state1 = - AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); - common::run_instruction( - Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), - state1, - ); -}