diff --git a/Cargo.lock b/Cargo.lock index 47b6c603c325..216840005748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,6 +469,7 @@ dependencies = [ "invalid-mutations 0.1.0", "libra-types 0.1.0", "mirai-annotations 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "move-core-types 0.1.0", "move-vm-types 0.1.0", "petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", diff --git a/language/bytecode-verifier/Cargo.toml b/language/bytecode-verifier/Cargo.toml index c59c3be3db9a..afa65c717374 100644 --- a/language/bytecode-verifier/Cargo.toml +++ b/language/bytecode-verifier/Cargo.toml @@ -17,6 +17,7 @@ petgraph = "0.5.0" borrow-graph = { path = "../borrow-graph", version = "0.0.1" } vm = { path = "../vm", version = "0.1.0" } libra-types = { path = "../../types", version = "0.1.0" } +move-core-types = { path = "../move-core/types", version = "0.1.0" } move-vm-types = { path = "../move-vm/types", version = "0.1.0" } [dev-dependencies] diff --git a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/bounds_tests.rs b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/bounds_tests.rs index 7329f143b960..c95d5953b6ff 100644 --- a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/bounds_tests.rs +++ b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/bounds_tests.rs @@ -16,29 +16,34 @@ fn empty_module_no_errors() { } #[test] -fn invalid_type_param_in_fn_return_types() { +fn invalid_type_param_in_fn_return_() { use SignatureToken::*; let mut m = basic_test_module(); - m.function_signatures[0].return_types = vec![TypeParameter(0)]; + m.function_handles[0].return_ = SignatureIndex(1); + m.signatures.push(Signature(vec![TypeParameter(0)])); + assert_eq!(m.signatures.len(), 2); m.freeze().unwrap_err(); } #[test] -fn invalid_type_param_in_fn_arg_types() { +fn invalid_type_param_in_fn_parameters() { use SignatureToken::*; let mut m = basic_test_module(); - m.function_signatures[0].arg_types = vec![TypeParameter(0)]; + m.function_handles[0].parameters = SignatureIndex(1); + m.signatures.push(Signature(vec![TypeParameter(0)])); m.freeze().unwrap_err(); } #[test] -fn invalid_struct_in_fn_return_types() { +fn invalid_struct_in_fn_return_() { use SignatureToken::*; let mut m = basic_test_module(); - m.function_signatures[0].return_types = vec![Struct(StructHandleIndex::new(1), vec![])]; + m.function_handles[0].return_ = SignatureIndex(1); + m.signatures + .push(Signature(vec![Struct(StructHandleIndex::new(1))])); m.freeze().unwrap_err(); } @@ -47,8 +52,13 @@ fn invalid_type_param_in_field() { use SignatureToken::*; let mut m = basic_test_module(); - m.type_signatures[0].0 = TypeParameter(0); - m.freeze().unwrap_err(); + match &mut m.struct_defs[0].field_information { + StructFieldInformation::Declared(ref mut fields) => { + fields[0].signature.0 = TypeParameter(0); + m.freeze().unwrap_err(); + } + _ => panic!("attempt to change a field that does not exist"), + } } #[test] @@ -56,8 +66,13 @@ fn invalid_struct_in_field() { use SignatureToken::*; let mut m = basic_test_module(); - m.type_signatures[0].0 = Struct(StructHandleIndex::new(3), vec![]); - m.freeze().unwrap_err(); + match &mut m.struct_defs[0].field_information { + StructFieldInformation::Declared(ref mut fields) => { + fields[0].signature.0 = Struct(StructHandleIndex::new(3)); + m.freeze().unwrap_err(); + } + _ => panic!("attempt to change a field that does not exist"), + } } #[test] @@ -65,8 +80,14 @@ fn invalid_struct_with_actuals_in_field() { use SignatureToken::*; let mut m = basic_test_module(); - m.type_signatures[0].0 = Struct(StructHandleIndex::new(0), vec![TypeParameter(0)]); - m.freeze().unwrap_err(); + match &mut m.struct_defs[0].field_information { + StructFieldInformation::Declared(ref mut fields) => { + fields[0].signature.0 = + StructInstantiation(StructHandleIndex::new(0), vec![TypeParameter(0)]); + m.freeze().unwrap_err(); + } + _ => panic!("attempt to change a field that does not exist"), + } } #[test] @@ -74,10 +95,12 @@ fn invalid_locals_id_in_call() { use Bytecode::*; let mut m = basic_test_module(); - m.function_defs[0].code.code = vec![Call( - FunctionHandleIndex::new(0), - LocalsSignatureIndex::new(1), - )]; + m.function_instantiations.push(FunctionInstantiation { + handle: FunctionHandleIndex::new(0), + type_parameters: SignatureIndex::new(1), + }); + let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); + m.function_defs[0].code.code = vec![CallGeneric(func_inst_idx)]; m.freeze().unwrap_err(); } @@ -87,12 +110,13 @@ fn invalid_type_param_in_call() { use SignatureToken::*; let mut m = basic_test_module(); - m.locals_signatures - .push(LocalsSignature(vec![TypeParameter(0)])); - m.function_defs[0].code.code = vec![Call( - FunctionHandleIndex::new(0), - LocalsSignatureIndex::new(1), - )]; + m.signatures.push(Signature(vec![TypeParameter(0)])); + m.function_instantiations.push(FunctionInstantiation { + handle: FunctionHandleIndex::new(0), + type_parameters: SignatureIndex::new(1), + }); + let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); + m.function_defs[0].code.code = vec![CallGeneric(func_inst_idx)]; m.freeze().unwrap_err(); } @@ -102,14 +126,14 @@ fn invalid_struct_as_type_actual_in_exists() { use SignatureToken::*; let mut m = basic_test_module(); - m.locals_signatures.push(LocalsSignature(vec![Struct( - StructHandleIndex::new(3), - vec![], - )])); - m.function_defs[0].code.code = vec![Call( - FunctionHandleIndex::new(0), - LocalsSignatureIndex::new(1), - )]; + m.signatures + .push(Signature(vec![Struct(StructHandleIndex::new(3))])); + m.function_instantiations.push(FunctionInstantiation { + handle: FunctionHandleIndex::new(0), + type_parameters: SignatureIndex::new(1), + }); + let func_inst_idx = FunctionInstantiationIndex(m.function_instantiations.len() as u16 - 1); + m.function_defs[0].code.code = vec![CallGeneric(func_inst_idx)]; m.freeze().unwrap_err(); } @@ -139,22 +163,14 @@ proptest! { module in CompiledModule::valid_strategy(20), oob_mutations in vec(OutOfBoundsMutation::strategy(), 0..40), ) { - let (module, mut expected_violations) = { + let (module, expected_violations) = { let oob_context = ApplyOutOfBoundsContext::new(module, oob_mutations); oob_context.apply() }; - expected_violations.sort(); let bounds_checker = BoundsChecker::new(&module); - let mut actual_violations = bounds_checker.verify(); - actual_violations.sort(); - for violation in actual_violations.iter_mut() { - violation.set_message("".to_string()) - } - for violation in expected_violations.iter_mut() { - violation.set_message("".to_string()) - } - prop_assert_eq!(expected_violations, actual_violations); + let actual_violations = bounds_checker.verify(); + prop_assert_eq!(expected_violations.is_empty(), actual_violations.is_empty()); } #[test] @@ -163,22 +179,15 @@ proptest! { mutations in vec(CodeUnitBoundsMutation::strategy(), 0..40), ) { let mut module = module.into_inner(); - let mut expected_violations = { + let expected_violations = { let context = ApplyCodeUnitBoundsContext::new(&mut module, mutations); context.apply() }; - expected_violations.sort(); let bounds_checker = BoundsChecker::new(&module); - let mut actual_violations = bounds_checker.verify(); - actual_violations.sort(); - for violation in actual_violations.iter_mut() { - violation.set_message("".to_string()) - } - for violation in expected_violations.iter_mut() { - violation.set_message("".to_string()) - } - prop_assert_eq!(expected_violations, actual_violations); + let actual_violations = bounds_checker.verify(); + + prop_assert_eq!(expected_violations.is_empty(), actual_violations.is_empty()); } #[test] diff --git a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/duplication_tests.rs b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/duplication_tests.rs index 2eb8d2cc6874..714346b7f51f 100644 --- a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/duplication_tests.rs +++ b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/duplication_tests.rs @@ -9,6 +9,6 @@ proptest! { #[test] fn valid_duplication(module in CompiledModule::valid_strategy(20)) { let duplication_checker = DuplicationChecker::new(&module); - prop_assert!(!duplication_checker.verify().is_empty()); + prop_assert!(duplication_checker.verify().is_empty()); } } diff --git a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/signature_tests.rs b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/signature_tests.rs index 8348a70860a7..4dea50f77a8a 100644 --- a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/signature_tests.rs +++ b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/signature_tests.rs @@ -2,19 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 use bytecode_verifier::{SignatureChecker, VerifiedModule}; -use invalid_mutations::signature::{ - ApplySignatureDoubleRefContext, ApplySignatureFieldRefContext, DoubleRefMutation, - FieldRefMutation, -}; -use libra_types::{account_address::AccountAddress, vm_error::StatusCode}; +use invalid_mutations::signature::{FieldRefMutation, SignatureRefMutation}; +use libra_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; -use proptest::{collection::vec, prelude::*}; +use proptest::{collection::vec, prelude::*, sample::Index as PropIndex}; use vm::file_format::{Bytecode::*, CompiledModule, SignatureToken::*, *}; #[test] fn test_reference_of_reference() { let mut m = basic_test_module(); - m.locals_signatures[0] = LocalsSignature(vec![Reference(Box::new(Reference(Box::new( + m.signatures[0] = Signature(vec![Reference(Box::new(Reference(Box::new( SignatureToken::Bool, ))))]); let errors = SignatureChecker::new(&m.freeze().unwrap()).verify(); @@ -31,64 +28,33 @@ proptest! { #[test] fn double_refs( module in CompiledModule::valid_strategy(20), - mutations in vec(DoubleRefMutation::strategy(), 0..40), + mutations in vec((any::(), any::()), 0..20), ) { let mut module = module.into_inner(); - let mut expected_violations = { - let context = ApplySignatureDoubleRefContext::new(&mut module, mutations); - context.apply() - }; - expected_violations.sort(); + let context = SignatureRefMutation::new(&mut module, mutations); + let expected_violations = context.apply(); let module = module.freeze().expect("should satisfy bounds checker"); let signature_checker = SignatureChecker::new(&module); - let actual_violations = signature_checker.verify(); - // Since some type signatures are field definition references as well, actual_violations - // will also contain VMStaticViolation::InvalidFieldDefReference errors -- filter those - // out. - let mut actual_violations: Vec<_> = actual_violations - .into_iter() - .filter(|err| err.major_status != StatusCode::INVALID_FIELD_DEF) - .collect(); - actual_violations.sort(); - // The error messages are slightly different from the invalid mutations, so clean these out - for violation in actual_violations.iter_mut() { - violation.set_message("".to_string()) - } - for violation in expected_violations.iter_mut() { - violation.set_message("".to_string()) - } - prop_assert_eq!(expected_violations, actual_violations); + + prop_assert_eq!(expected_violations, !actual_violations.is_empty()); } #[test] fn field_def_references( module in CompiledModule::valid_strategy(20), - mutations in vec(FieldRefMutation::strategy(), 0..40), + mutations in vec((any::(), any::()), 0..40), ) { let mut module = module.into_inner(); - let mut expected_violations = { - let context = ApplySignatureFieldRefContext::new(&mut module, mutations); - context.apply() - }; - expected_violations.sort(); + let context = FieldRefMutation::new(&mut module, mutations); + let expected_violations = context.apply(); let module = module.freeze().expect("should satisfy bounds checker"); let signature_checker = SignatureChecker::new(&module); + let actual_violations = signature_checker.verify(); - let mut actual_violations = signature_checker.verify(); - // Note that this shouldn't cause any InvalidSignatureToken errors because there are no - // double references involved. So no filtering is required here. - actual_violations.sort(); - // The error messages are slightly different from the invalid mutations, so clean these out - for violation in actual_violations.iter_mut() { - violation.set_message("".to_string()) - } - for violation in expected_violations.iter_mut() { - violation.set_message("".to_string()) - } - prop_assert_eq!(expected_violations, actual_violations); + prop_assert_eq!(expected_violations, !actual_violations.is_empty()); } } @@ -100,32 +66,31 @@ fn no_verify_locals_good() { name: IdentifierIndex(0), }], struct_handles: vec![], + signatures: vec![ + Signature(vec![Address]), + Signature(vec![U64]), + Signature(vec![]), + ], function_handles: vec![ FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), + return_: SignatureIndex(2), + parameters: SignatureIndex(0), + type_parameters: vec![], }, FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(2), - signature: FunctionSignatureIndex(1), + return_: SignatureIndex(2), + parameters: SignatureIndex(1), + type_parameters: vec![], }, ], - type_signatures: vec![], - function_signatures: vec![ - FunctionSignature { - return_types: vec![], - arg_types: vec![Address], - type_formals: vec![], - }, - FunctionSignature { - return_types: vec![], - arg_types: vec![U64], - type_formals: vec![], - }, - ], - locals_signatures: vec![LocalsSignature(vec![Address]), LocalsSignature(vec![U64])], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], identifiers: vec![ Identifier::new("Bad").unwrap(), Identifier::new("blah").unwrap(), @@ -134,7 +99,6 @@ fn no_verify_locals_good() { byte_array_pool: vec![], address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], struct_defs: vec![], - field_defs: vec![], function_defs: vec![ FunctionDefinition { function: FunctionHandleIndex(0), @@ -142,7 +106,7 @@ fn no_verify_locals_good() { acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Ret], }, }, @@ -152,7 +116,7 @@ fn no_verify_locals_good() { acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(1), + locals: SignatureIndex(1), code: vec![Ret], }, }, @@ -173,36 +137,39 @@ fn no_verify_locals_bad1() { name: IdentifierIndex(0), }], struct_handles: vec![], + signatures: vec![ + Signature(vec![U64]), + Signature(vec![Address]), + Signature(vec![]), + ], function_handles: vec![FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), - }], - type_signatures: vec![], - function_signatures: vec![FunctionSignature { - return_types: vec![], - arg_types: vec![Address], - type_formals: vec![], + parameters: SignatureIndex(1), + return_: SignatureIndex(2), + type_parameters: vec![], }], - locals_signatures: vec![LocalsSignature(vec![U64])], - identifiers: vec![ - Identifier::new("Bad").unwrap(), - Identifier::new("blah").unwrap(), - ], - byte_array_pool: vec![], - address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], - struct_defs: vec![], - field_defs: vec![], function_defs: vec![FunctionDefinition { function: FunctionHandleIndex(0), flags: 1, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Ret], }, }], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], + identifiers: vec![ + Identifier::new("Bad").unwrap(), + Identifier::new("blah").unwrap(), + ], + byte_array_pool: vec![], + address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], + struct_defs: vec![], }; assert!(VerifiedModule::new(compiled_module_bad1.freeze().unwrap()).is_err()); } @@ -218,36 +185,35 @@ fn no_verify_locals_bad2() { name: IdentifierIndex(0), }], struct_handles: vec![], + signatures: vec![Signature(vec![]), Signature(vec![Address])], function_handles: vec![FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), - }], - type_signatures: vec![], - function_signatures: vec![FunctionSignature { - return_types: vec![], - arg_types: vec![Address], - type_formals: vec![], + parameters: SignatureIndex(1), + return_: SignatureIndex(0), + type_parameters: vec![], }], - locals_signatures: vec![LocalsSignature(vec![])], - identifiers: vec![ - Identifier::new("Bad").unwrap(), - Identifier::new("blah").unwrap(), - ], - byte_array_pool: vec![], - address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], - struct_defs: vec![], - field_defs: vec![], function_defs: vec![FunctionDefinition { function: FunctionHandleIndex(0), flags: 1, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Ret], }, }], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], + identifiers: vec![ + Identifier::new("Bad").unwrap(), + Identifier::new("blah").unwrap(), + ], + byte_array_pool: vec![], + address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], + struct_defs: vec![], }; assert!(VerifiedModule::new(compiled_module_bad2.freeze().unwrap()).is_err()); } @@ -264,18 +230,22 @@ fn no_verify_locals_bad3() { name: IdentifierIndex(0), }], struct_handles: vec![], + signatures: vec![ + Signature(vec![U64, Address]), + Signature(vec![]), + Signature(vec![Address]), + ], function_handles: vec![FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), - }], - type_signatures: vec![], - function_signatures: vec![FunctionSignature { - return_types: vec![], - arg_types: vec![Address], - type_formals: vec![], + return_: SignatureIndex(1), + parameters: SignatureIndex(2), + type_parameters: vec![], }], - locals_signatures: vec![LocalsSignature(vec![U64, Address])], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], identifiers: vec![ Identifier::new("Bad").unwrap(), Identifier::new("blah").unwrap(), @@ -283,14 +253,13 @@ fn no_verify_locals_bad3() { byte_array_pool: vec![], address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], struct_defs: vec![], - field_defs: vec![], function_defs: vec![FunctionDefinition { function: FunctionHandleIndex(0), flags: 1, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Ret], }, }], @@ -310,18 +279,22 @@ fn no_verify_locals_bad4() { name: IdentifierIndex(0), }], struct_handles: vec![], + signatures: vec![ + Signature(vec![U64, U64, Address]), + Signature(vec![]), + Signature(vec![U64, Address]), + ], function_handles: vec![FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), - }], - type_signatures: vec![], - function_signatures: vec![FunctionSignature { - return_types: vec![], - arg_types: vec![U64, Address], - type_formals: vec![], + return_: SignatureIndex(1), + parameters: SignatureIndex(2), + type_parameters: vec![], }], - locals_signatures: vec![LocalsSignature(vec![U64, U64, Address])], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], identifiers: vec![ Identifier::new("Bad").unwrap(), Identifier::new("blah").unwrap(), @@ -329,14 +302,13 @@ fn no_verify_locals_bad4() { byte_array_pool: vec![], address_pool: vec![AccountAddress::new([0; AccountAddress::LENGTH])], struct_defs: vec![], - field_defs: vec![], function_defs: vec![FunctionDefinition { function: FunctionHandleIndex(0), flags: 1, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Ret], }, }], diff --git a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/unused_entry_tests.rs b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/unused_entry_tests.rs index a7d3e7228368..4d5700ae66fe 100644 --- a/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/unused_entry_tests.rs +++ b/language/bytecode-verifier/bytecode_verifier_tests/src/unit_tests/unused_entry_tests.rs @@ -1,78 +1,17 @@ // Copyright (c) The Libra Core Contributors // SPDX-License-Identifier: Apache-2.0 -use bytecode_verifier::UnusedEntryChecker; -use libra_types::vm_error::StatusCode; -use move_core_types::identifier::Identifier; +use bytecode_verifier::unused_entries::UnusedEntryChecker; use proptest::prelude::*; -use vm::file_format::{ - CompiledModule, FieldDefinition, IdentifierIndex, LocalsSignature, ModuleHandleIndex, - SignatureToken, StructHandle, StructHandleIndex, TypeSignature, TypeSignatureIndex, -}; +use vm::file_format::{CompiledModule, Signature}; proptest! { #[test] - fn unused_locals_signature(module in CompiledModule::valid_strategy(10)) { + fn unused_signature(module in CompiledModule::valid_strategy(10)) { let mut module = module.into_inner(); - module.locals_signatures.push(LocalsSignature(vec![])); + module.signatures.push(Signature(vec![])); let module = module.freeze().unwrap(); let unused_entry_checker = UnusedEntryChecker::new(&module); prop_assert!(!unused_entry_checker.verify().is_empty()); } } - -proptest! { - #[test] - fn unused_type_signature(module in CompiledModule::valid_strategy(10)) { - let mut module = module.into_inner(); - module.type_signatures.push(TypeSignature(SignatureToken::Bool)); - let module = module.freeze().unwrap(); - let unused_entry_checker = UnusedEntryChecker::new(&module); - prop_assert!(!unused_entry_checker.verify().is_empty()); - } -} - -proptest! { - #[test] - fn unused_field(module in CompiledModule::valid_strategy(10)) { - let mut module = module.into_inner(); - - let type_sig_idx = module.type_signatures.len() as u16; - module.type_signatures.push(TypeSignature(SignatureToken::Bool)); - - let struct_name_idx = module.identifiers.len() as u16; - module.identifiers.push(Identifier::new("foo".to_string()).unwrap()); - - let field_name_idx = module.identifiers.len() as u16; - module.identifiers.push(Identifier::new("bar".to_string()).unwrap()); - - let sh_idx = module.struct_handles.len() as u16; - module.struct_handles.push(StructHandle{ - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(struct_name_idx), - is_nominal_resource: false, - type_formals: vec![], - }); - - module.field_defs.push(FieldDefinition{ - struct_: StructHandleIndex::new(sh_idx), - name: IdentifierIndex::new(field_name_idx), - signature: TypeSignatureIndex::new(type_sig_idx), - }); - - let module = module.freeze().unwrap(); - let unused_entry_checker = UnusedEntryChecker::new(&module); - - let errs = unused_entry_checker.verify(); - - let has_unused_fields = errs.iter().any(|err| { - matches!(err.major_status, StatusCode::UNUSED_FIELD) - }); - - let has_unused_type_signature = errs.iter().any(|err| { - matches!(err.major_status, StatusCode::UNUSED_TYPE_SIGNATURE) - }); - - prop_assert!(has_unused_fields && has_unused_type_signature); - } -} diff --git a/language/bytecode-verifier/invalid-mutations/src/bounds.rs b/language/bytecode-verifier/invalid-mutations/src/bounds.rs index 5030ac77eaea..259cf12e1432 100644 --- a/language/bytecode-verifier/invalid-mutations/src/bounds.rs +++ b/language/bytecode-verifier/invalid-mutations/src/bounds.rs @@ -11,10 +11,8 @@ use std::collections::BTreeMap; use vm::{ errors::{append_err_info, bounds_error}, file_format::{ - AddressPoolIndex, CompiledModule, CompiledModuleMut, FieldDefinitionIndex, - FunctionHandleIndex, FunctionSignatureIndex, IdentifierIndex, LocalsSignatureIndex, - ModuleHandleIndex, StructFieldInformation, StructHandleIndex, TableIndex, - TypeSignatureIndex, + AddressPoolIndex, CompiledModule, CompiledModuleMut, FunctionHandleIndex, IdentifierIndex, + ModuleHandleIndex, SignatureIndex, StructHandleIndex, TableIndex, }, internals::ModuleIndex, views::{ModuleView, SignatureTokenView}, @@ -23,6 +21,7 @@ use vm::{ mod code_unit; pub use code_unit::{ApplyCodeUnitBoundsContext, CodeUnitBoundsMutation}; +use vm::file_format::SignatureToken; /// Represents the number of pointers that exist out from a node of a particular kind. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -51,22 +50,17 @@ impl PointerKind { match src_kind { ModuleHandle => &[One(AddressPool), One(Identifier)], StructHandle => &[One(ModuleHandle), One(Identifier)], - FunctionHandle => &[One(ModuleHandle), One(Identifier), One(FunctionSignature)], - StructDefinition => &[One(StructHandle), One(FieldDefinition)], - FieldDefinition => &[One(StructHandle), One(Identifier), One(TypeSignature)], - FunctionDefinition => &[One(FunctionHandle), One(LocalsSignature)], - TypeSignature => &[Optional(StructHandle)], - FunctionSignature => &[Star(StructHandle)], - LocalsSignature => &[Star(StructHandle)], - Identifier => &[], - ByteArrayPool => &[], - AddressPool => &[], - // LocalPool and CodeDefinition are function-local, and this only works for - // module-scoped indexes. - // XXX maybe don't treat LocalPool and CodeDefinition the same way as the others? - LocalPool => &[], - CodeDefinition => &[], - TypeParameter => &[], + FunctionHandle => &[ + One(ModuleHandle), + One(Identifier), + One(Signature), + One(Signature), + ], + StructDefinition => &[One(StructHandle), Star(StructHandle)], + FunctionDefinition => &[One(FunctionHandle), One(Signature)], + Signature => &[Star(StructHandle)], + FieldHandle => &[One(StructHandle)], + _ => &[], } } @@ -82,12 +76,10 @@ pub static VALID_POINTER_SRCS: &[IndexKind] = &[ IndexKind::ModuleHandle, IndexKind::StructHandle, IndexKind::FunctionHandle, + IndexKind::FieldHandle, IndexKind::StructDefinition, - IndexKind::FieldDefinition, IndexKind::FunctionDefinition, - IndexKind::TypeSignature, - IndexKind::FunctionSignature, - IndexKind::LocalsSignature, + IndexKind::Signature, ]; #[cfg(test)] @@ -172,23 +164,17 @@ pub struct ApplyOutOfBoundsContext { mutations: Option>, // Some precomputations done for signatures. - type_sig_structs: Vec, - function_sig_structs: Vec, - locals_sig_structs: Vec<(LocalsSignatureIndex, usize)>, + sig_structs: Vec<(SignatureIndex, usize)>, } impl ApplyOutOfBoundsContext { pub fn new(module: CompiledModule, mutations: Vec) -> Self { - let type_sig_structs: Vec<_> = Self::type_sig_structs(&module).collect(); - let function_sig_structs: Vec<_> = Self::function_sig_structs(&module).collect(); - let locals_sig_structs: Vec<_> = Self::locals_sig_structs(&module).collect(); + let sig_structs: Vec<_> = Self::sig_structs(&module).collect(); Self { module: module.into_inner(), mutations: Some(mutations), - type_sig_structs, - function_sig_structs, - locals_sig_structs, + sig_structs, } } @@ -225,11 +211,7 @@ impl ApplyOutOfBoundsContext { mutations: Vec, ) -> Vec { let src_count = match src_kind { - // Only the signature indexes that have structs in them (i.e. are in *_sig_structs) - // are going to be modifiable, so pick among them. - IndexKind::TypeSignature => self.type_sig_structs.len(), - IndexKind::FunctionSignature => self.function_sig_structs.len(), - IndexKind::LocalsSignature => self.locals_sig_structs.len(), + IndexKind::Signature => self.sig_structs.len(), // For the other sorts it's always possible to change an index. src_kind => self.module.kind_count(src_kind), }; @@ -270,7 +252,7 @@ impl ApplyOutOfBoundsContext { // These are default values, but some of the match arms below mutate them. let mut src_idx = src_idx; - let mut err = bounds_error( + let err = bounds_error( dst_kind, new_idx as usize, dst_count, @@ -284,102 +266,40 @@ impl ApplyOutOfBoundsContext { match (src_kind, dst_kind) { (ModuleHandle, AddressPool) => { - self.module.module_handles[src_idx].address = AddressPoolIndex::new(new_idx); + self.module.module_handles[src_idx].address = AddressPoolIndex(new_idx); } (ModuleHandle, Identifier) => { - self.module.module_handles[src_idx].name = IdentifierIndex::new(new_idx) + self.module.module_handles[src_idx].name = IdentifierIndex(new_idx) } (StructHandle, ModuleHandle) => { - self.module.struct_handles[src_idx].module = ModuleHandleIndex::new(new_idx) + self.module.struct_handles[src_idx].module = ModuleHandleIndex(new_idx) } (StructHandle, Identifier) => { - self.module.struct_handles[src_idx].name = IdentifierIndex::new(new_idx) + self.module.struct_handles[src_idx].name = IdentifierIndex(new_idx) } (FunctionHandle, ModuleHandle) => { - self.module.function_handles[src_idx].module = ModuleHandleIndex::new(new_idx) + self.module.function_handles[src_idx].module = ModuleHandleIndex(new_idx) } (FunctionHandle, Identifier) => { - self.module.function_handles[src_idx].name = IdentifierIndex::new(new_idx) + self.module.function_handles[src_idx].name = IdentifierIndex(new_idx) } - (FunctionHandle, FunctionSignature) => { - self.module.function_handles[src_idx].signature = - FunctionSignatureIndex::new(new_idx) + (FunctionHandle, Signature) => { + self.module.function_handles[src_idx].parameters = SignatureIndex(new_idx); } (StructDefinition, StructHandle) => { - self.module.struct_defs[src_idx].struct_handle = StructHandleIndex::new(new_idx) - } - (StructDefinition, FieldDefinition) => { - let field_count = match self.module.struct_defs[src_idx].field_information { - // There is no way to set an invalid index for a native struct definition - StructFieldInformation::Native => return None, - StructFieldInformation::Declared { field_count, .. } => field_count, - }; - - // Consider a situation with 3 fields, and with first field = 1 and count = 2. - // A graphical representation of that might be: - // - // |___|___|___| - // idx 0 1 2 - // ^ ^ - // | | - // first field = 1 (first field + count) = 3 - // - // Given that the lowest value for new_idx is 3 (offset 0), the goal is to make - // (first field + count) at least 4, or (new_idx + 1). This means that the first - // field would be new_idx + 1 - count. - let end_idx = new_idx + 1; - let first_new_idx = end_idx - field_count; - let field_information = StructFieldInformation::Declared { - field_count, - fields: FieldDefinitionIndex::new(first_new_idx), - }; - self.module.struct_defs[src_idx].field_information = field_information; - err = VMStatus::new(StatusCode::RANGE_OUT_OF_BOUNDS); - err.set_message(format!( - "Range {}-{} out of bounds for {} while indexing {}", - dst_kind, dst_count, first_new_idx as usize, end_idx as usize, - )); - } - (FieldDefinition, StructHandle) => { - self.module.field_defs[src_idx].struct_ = StructHandleIndex::new(new_idx) - } - (FieldDefinition, Identifier) => { - self.module.field_defs[src_idx].name = IdentifierIndex::new(new_idx) - } - (FieldDefinition, TypeSignature) => { - self.module.field_defs[src_idx].signature = TypeSignatureIndex::new(new_idx) + self.module.struct_defs[src_idx].struct_handle = StructHandleIndex(new_idx) } (FunctionDefinition, FunctionHandle) => { - self.module.function_defs[src_idx].function = FunctionHandleIndex::new(new_idx) - } - (FunctionDefinition, LocalsSignature) => { - self.module.function_defs[src_idx].code.locals = LocalsSignatureIndex::new(new_idx) + self.module.function_defs[src_idx].function = FunctionHandleIndex(new_idx) } - (TypeSignature, StructHandle) => { - // For this and the other signatures, the source index will be picked from - // only the ones that have struct handles in them. - src_idx = self.type_sig_structs[src_idx].into_index(); - self.module.type_signatures[src_idx] - .0 - .debug_set_sh_idx(StructHandleIndex::new(new_idx)); + (FunctionDefinition, Signature) => { + self.module.function_defs[src_idx].code.locals = SignatureIndex(new_idx) } - (FunctionSignature, StructHandle) => match &self.function_sig_structs[src_idx] { - FunctionSignatureTokenIndex::ReturnType(actual_src_idx, ret_idx) => { - src_idx = actual_src_idx.into_index(); - self.module.function_signatures[src_idx].return_types[*ret_idx] - .debug_set_sh_idx(StructHandleIndex::new(new_idx)); - } - FunctionSignatureTokenIndex::ArgType(actual_src_idx, arg_idx) => { - src_idx = actual_src_idx.into_index(); - self.module.function_signatures[src_idx].arg_types[*arg_idx] - .debug_set_sh_idx(StructHandleIndex::new(new_idx)); - } - }, - (LocalsSignature, StructHandle) => { - let (actual_src_idx, arg_idx) = self.locals_sig_structs[src_idx]; + (Signature, StructHandle) => { + let (actual_src_idx, arg_idx) = self.sig_structs[src_idx]; src_idx = actual_src_idx.into_index(); - self.module.locals_signatures[src_idx].0[arg_idx] - .debug_set_sh_idx(StructHandleIndex::new(new_idx)); + self.module.signatures[src_idx].0[arg_idx] + .debug_set_sh_idx(StructHandleIndex(new_idx)); } _ => panic!("Invalid pointer kind: {:?} -> {:?}", src_kind, dst_kind), } @@ -387,60 +307,16 @@ impl ApplyOutOfBoundsContext { Some(append_err_info(err, src_kind, src_idx)) } - /// Returns the indexes of type signatures that contain struct handles inside them. - fn type_sig_structs<'b>( - module: &'b CompiledModule, - ) -> impl Iterator + 'b { - let module_view = ModuleView::new(module); - module_view - .type_signatures() - .enumerate() - .filter_map(|(idx, signature)| { - signature - .token() - .struct_handle() - .map(|_| TypeSignatureIndex::new(idx as u16)) - }) - } - - /// Returns the indexes of function signatures that contain struct handles inside them. - fn function_sig_structs<'b>( - module: &'b CompiledModule, - ) -> impl Iterator + 'b { - let module_view = ModuleView::new(module); - let return_tokens = - module_view - .function_signatures() - .enumerate() - .flat_map(|(idx, signature)| { - let idx = FunctionSignatureIndex::new(idx as u16); - Self::find_struct_tokens(signature.return_tokens(), move |arg_idx| { - FunctionSignatureTokenIndex::ReturnType(idx, arg_idx) - }) - }); - let arg_tokens = - module_view - .function_signatures() - .enumerate() - .flat_map(|(idx, signature)| { - let idx = FunctionSignatureIndex::new(idx as u16); - Self::find_struct_tokens(signature.arg_tokens(), move |arg_idx| { - FunctionSignatureTokenIndex::ArgType(idx, arg_idx) - }) - }); - return_tokens.chain(arg_tokens) - } - /// Returns the indexes of locals signatures that contain struct handles inside them. - fn locals_sig_structs<'b>( + fn sig_structs<'b>( module: &'b CompiledModule, - ) -> impl Iterator + 'b { + ) -> impl Iterator + 'b { let module_view = ModuleView::new(module); module_view - .locals_signatures() + .signatures() .enumerate() .flat_map(|(idx, signature)| { - let idx = LocalsSignatureIndex::new(idx as u16); + let idx = SignatureIndex(idx as u16); Self::find_struct_tokens(signature.tokens(), move |arg_idx| (idx, arg_idx)) }) } @@ -456,12 +332,19 @@ impl ApplyOutOfBoundsContext { tokens .into_iter() .enumerate() - .filter_map(move |(arg_idx, token)| token.struct_handle().map(|_| map_fn(arg_idx))) + .filter_map(move |(arg_idx, token)| { + struct_handle(token.signature_token()).map(|_| map_fn(arg_idx)) + }) } } -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -enum FunctionSignatureTokenIndex { - ReturnType(FunctionSignatureIndex, usize), - ArgType(FunctionSignatureIndex, usize), +fn struct_handle(token: &SignatureToken) -> Option { + use SignatureToken::*; + + match token { + Struct(sh_idx) => Some(*sh_idx), + StructInstantiation(sh_idx, _) => Some(*sh_idx), + Reference(token) | MutableReference(token) => struct_handle(token), + Bool | U8 | U64 | U128 | Address | Vector(_) | TypeParameter(_) => None, + } } diff --git a/language/bytecode-verifier/invalid-mutations/src/bounds/code_unit.rs b/language/bytecode-verifier/invalid-mutations/src/bounds/code_unit.rs index f9163909e43b..6cd182e527db 100644 --- a/language/bytecode-verifier/invalid-mutations/src/bounds/code_unit.rs +++ b/language/bytecode-verifier/invalid-mutations/src/bounds/code_unit.rs @@ -9,8 +9,8 @@ use vm::{ errors::{append_err_info, bytecode_offset_err}, file_format::{ AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, CompiledModuleMut, - FieldDefinitionIndex, FunctionHandleIndex, LocalIndex, StructDefinitionIndex, TableIndex, - NO_TYPE_ACTUALS, + FieldHandleIndex, FieldInstantiationIndex, FunctionHandleIndex, FunctionInstantiationIndex, + LocalIndex, StructDefInstantiationIndex, StructDefinitionIndex, TableIndex, }, internals::ModuleIndex, IndexKind, @@ -71,8 +71,7 @@ macro_rules! struct_bytecode { let dst_len = $dst_len; let new_idx = (dst_len + $offset) as TableIndex; ( - // TODO: check this again once generics is implemented - $bytecode_ident($idx_type::new(new_idx), NO_TYPE_ACTUALS), + $bytecode_ident($idx_type::new(new_idx)), bytecode_offset_err( $idx_type::KIND, new_idx as usize, @@ -156,7 +155,7 @@ impl<'a> ApplyCodeUnitBoundsContext<'a> { let code = &mut self.module.function_defs[idx].code; ( code.code.len(), - self.module.locals_signatures[code.locals.into_index()].len(), + self.module.signatures[code.locals.into_index()].len(), ) }; @@ -170,8 +169,11 @@ impl<'a> ApplyCodeUnitBoundsContext<'a> { let address_pool_len = self.module.address_pool.len(); let byte_array_pool_len = self.module.byte_array_pool.len(); let function_handles_len = self.module.function_handles.len(); - let field_defs_len = self.module.field_defs.len(); + let field_handle_len = self.module.field_handles.len(); let struct_defs_len = self.module.struct_defs.len(); + let struct_inst_len = self.module.struct_def_instantiations.len(); + let function_inst_len = self.module.function_instantiations.len(); + let field_inst_len = self.module.field_instantiations.len(); mutations .iter() @@ -197,75 +199,145 @@ impl<'a> ApplyCodeUnitBoundsContext<'a> { LdByteArray ), ImmBorrowField(_) => new_bytecode!( - field_defs_len, + field_handle_len, bytecode_idx, offset, - FieldDefinitionIndex, + FieldHandleIndex, ImmBorrowField ), + ImmBorrowFieldGeneric(_) => new_bytecode!( + field_inst_len, + bytecode_idx, + offset, + FieldInstantiationIndex, + ImmBorrowFieldGeneric + ), MutBorrowField(_) => new_bytecode!( - field_defs_len, + field_handle_len, bytecode_idx, offset, - FieldDefinitionIndex, + FieldHandleIndex, MutBorrowField ), - Call(_, _) => struct_bytecode!( + MutBorrowFieldGeneric(_) => new_bytecode!( + field_inst_len, + bytecode_idx, + offset, + FieldInstantiationIndex, + MutBorrowFieldGeneric + ), + Call(_) => struct_bytecode!( function_handles_len, bytecode_idx, offset, FunctionHandleIndex, Call ), - Pack(_, _) => struct_bytecode!( + CallGeneric(_) => struct_bytecode!( + function_inst_len, + bytecode_idx, + offset, + FunctionInstantiationIndex, + CallGeneric + ), + Pack(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, Pack ), - Unpack(_, _) => struct_bytecode!( + PackGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + PackGeneric + ), + Unpack(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, Unpack ), - Exists(_, _) => struct_bytecode!( + UnpackGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + UnpackGeneric + ), + Exists(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, Exists ), - MutBorrowGlobal(_, _) => struct_bytecode!( + ExistsGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + ExistsGeneric + ), + MutBorrowGlobal(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, MutBorrowGlobal ), - ImmBorrowGlobal(_, _) => struct_bytecode!( + MutBorrowGlobalGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + MutBorrowGlobalGeneric + ), + ImmBorrowGlobal(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, ImmBorrowGlobal ), - MoveFrom(_, _) => struct_bytecode!( + ImmBorrowGlobalGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + ImmBorrowGlobalGeneric + ), + MoveFrom(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, MoveFrom ), - MoveToSender(_, _) => struct_bytecode!( + MoveFromGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + MoveFromGeneric + ), + MoveToSender(_) => struct_bytecode!( struct_defs_len, bytecode_idx, offset, StructDefinitionIndex, MoveToSender ), + MoveToSenderGeneric(_) => struct_bytecode!( + struct_inst_len, + bytecode_idx, + offset, + StructDefInstantiationIndex, + MoveToSenderGeneric + ), BrTrue(_) => code_bytecode!(code_len, bytecode_idx, offset, BrTrue), BrFalse(_) => code_bytecode!(code_len, bytecode_idx, offset, BrFalse), Branch(_) => code_bytecode!(code_len, bytecode_idx, offset, Branch), @@ -306,15 +378,25 @@ fn is_interesting(bytecode: &Bytecode) -> bool { LdAddr(_) | LdByteArray(_) | ImmBorrowField(_) + | ImmBorrowFieldGeneric(_) | MutBorrowField(_) - | Call(_, _) - | Pack(_, _) - | Unpack(_, _) - | Exists(_, _) - | MutBorrowGlobal(_, _) - | ImmBorrowGlobal(_, _) - | MoveFrom(_, _) - | MoveToSender(_, _) + | MutBorrowFieldGeneric(_) + | Call(_) + | CallGeneric(_) + | Pack(_) + | PackGeneric(_) + | Unpack(_) + | UnpackGeneric(_) + | Exists(_) + | ExistsGeneric(_) + | MutBorrowGlobal(_) + | MutBorrowGlobalGeneric(_) + | ImmBorrowGlobal(_) + | ImmBorrowGlobalGeneric(_) + | MoveFrom(_) + | MoveFromGeneric(_) + | MoveToSender(_) + | MoveToSenderGeneric(_) | BrTrue(_) | BrFalse(_) | Branch(_) diff --git a/language/bytecode-verifier/invalid-mutations/src/signature.rs b/language/bytecode-verifier/invalid-mutations/src/signature.rs index dfedbfa9e8d2..65042bdf8a96 100644 --- a/language/bytecode-verifier/invalid-mutations/src/signature.rs +++ b/language/bytecode-verifier/invalid-mutations/src/signature.rs @@ -1,271 +1,99 @@ // Copyright (c) The Libra Core Contributors // SPDX-License-Identifier: Apache-2.0 -use libra_proptest_helpers::{pick_slice_idxs, RepeatVec}; -use libra_types::vm_error::{StatusCode, VMStatus}; -use proptest::{ - prelude::*, - sample::{select, Index as PropIndex}, +use proptest::sample::Index as PropIndex; +use vm::file_format::{ + CompiledModuleMut, Signature, SignatureToken, StructFieldInformation, TypeSignature, }; -use std::{collections::BTreeMap, iter}; -use vm::{ - errors::append_err_info, - file_format::{CompiledModuleMut, SignatureToken}, - internals::ModuleIndex, - IndexKind, SignatureTokenKind, -}; - -/// Represents a mutation that wraps a signature token up in a double reference (or an array of -/// references. -#[derive(Clone, Debug)] -pub struct DoubleRefMutation { - idx: PropIndex, - kind: DoubleRefMutationKind, -} - -impl DoubleRefMutation { - pub fn strategy() -> impl Strategy { - (any::(), DoubleRefMutationKind::strategy()) - .prop_map(|(idx, kind)| Self { idx, kind }) - } -} - -impl AsRef for DoubleRefMutation { - #[inline] - fn as_ref(&self) -> &PropIndex { - &self.idx - } -} -/// Context for applying a list of `DoubleRefMutation` instances. -pub struct ApplySignatureDoubleRefContext<'a> { +pub struct SignatureRefMutation<'a> { module: &'a mut CompiledModuleMut, - mutations: Vec, + mutations: Vec<(PropIndex, PropIndex)>, } -impl<'a> ApplySignatureDoubleRefContext<'a> { - pub fn new(module: &'a mut CompiledModuleMut, mutations: Vec) -> Self { +impl<'a> SignatureRefMutation<'a> { + pub fn new(module: &'a mut CompiledModuleMut, mutations: Vec<(PropIndex, PropIndex)>) -> Self { Self { module, mutations } } - pub fn apply(self) -> Vec { - // Apply double refs before field refs -- XXX is this correct? - let sig_indexes = self.all_sig_indexes(); - let picked = sig_indexes.pick_uniform(&self.mutations); - - let mut errs = vec![]; - - for (double_ref, (sig_idx, idx2)) in self.mutations.iter().zip(picked) { - // When there's one level of indexing (e.g. Type), idx2 represents that level. - // When there's two levels of indexing (e.g. FunctionArg), idx1 represents the outer - // level (signature index) and idx2 the inner level (token index). - let (token, kind, error_idx) = match sig_idx { - SignatureIndex::Type => ( - &mut self.module.type_signatures[idx2].0, - IndexKind::TypeSignature, - idx2, - ), - SignatureIndex::FunctionReturn(idx1) => ( - &mut self.module.function_signatures[*idx1].return_types[idx2], - IndexKind::FunctionSignature, - *idx1, - ), - SignatureIndex::FunctionArg(idx1) => ( - &mut self.module.function_signatures[*idx1].arg_types[idx2], - IndexKind::FunctionSignature, - *idx1, - ), - SignatureIndex::Locals(idx1) => ( - &mut self.module.locals_signatures[*idx1].0[idx2], - IndexKind::LocalsSignature, - *idx1, - ), - }; - - *token = double_ref.kind.wrap(token.clone()); - let msg = format!( - "At index {} with kind {} with token {:#?}, with outer kind {} and inner kind {}", - error_idx, - kind, - token.clone(), - double_ref.kind.outer, - double_ref.kind.inner - ); - // If a locals signature is used by more than one functions and contains an error, it - // should be reported multiple times. The following code emulates this behavior to match - // the new implementation of the signature checker. - // TODO: Revisit this and see if it's still required once we rework prop tests. - match sig_idx { - SignatureIndex::Locals(idx) => { - let n_references = self - .module - .function_defs - .iter() - .filter(|def| !def.is_native() && def.code.locals.0 as usize == *idx) - .count(); - errs.extend( - iter::repeat_with(|| { - VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN) - .with_message(msg.clone()) - }) - .take(n_references), - ); - } - _ => { - errs.push(VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN).with_message(msg)); - } - } - } - - errs - } - - fn all_sig_indexes(&self) -> RepeatVec { - let mut res = RepeatVec::new(); - res.extend(SignatureIndex::Type, self.module.type_signatures.len()); - for (idx, sig) in self.module.function_signatures.iter().enumerate() { - res.extend(SignatureIndex::FunctionReturn(idx), sig.return_types.len()); - } - for (idx, sig) in self.module.function_signatures.iter().enumerate() { - res.extend(SignatureIndex::FunctionArg(idx), sig.arg_types.len()); + pub fn apply(self) -> bool { + if self.module.signatures.is_empty() { + return false; } - for (idx, sig) in self.module.locals_signatures.iter().enumerate() { - res.extend(SignatureIndex::Locals(idx), sig.0.len()); + let module = self.module; + let mut mutations = false; + for (s_idx, t_idx) in self.mutations { + let sig_idx = s_idx.index(module.signatures.len()); + let sig = &module.signatures[sig_idx]; + if sig.is_empty() { + continue; + } + let token_idx = t_idx.index(sig.len()); + let new_sig = mutate_sig(sig, token_idx); + module.signatures[sig_idx] = new_sig; + mutations = true; } - res - } -} - -/// Represents a mutation that turns a field definition's type into a reference. -#[derive(Clone, Debug)] -pub struct FieldRefMutation { - idx: PropIndex, - is_mutable: bool, -} - -impl FieldRefMutation { - pub fn strategy() -> impl Strategy { - (any::(), any::()).prop_map(|(idx, is_mutable)| Self { idx, is_mutable }) - } -} - -impl AsRef for FieldRefMutation { - #[inline] - fn as_ref(&self) -> &PropIndex { - &self.idx + mutations } } /// Context for applying a list of `FieldRefMutation` instances. -pub struct ApplySignatureFieldRefContext<'a> { +pub struct FieldRefMutation<'a> { module: &'a mut CompiledModuleMut, - mutations: Vec, + mutations: Vec<(PropIndex, PropIndex)>, } -impl<'a> ApplySignatureFieldRefContext<'a> { - pub fn new(module: &'a mut CompiledModuleMut, mutations: Vec) -> Self { +impl<'a> FieldRefMutation<'a> { + pub fn new(module: &'a mut CompiledModuleMut, mutations: Vec<(PropIndex, PropIndex)>) -> Self { Self { module, mutations } } #[inline] - pub fn apply(self) -> Vec { - // One field definition might be associated with more than one signature, so collect all - // the interesting ones in a map of type_sig_idx => field_def_idx. - let mut interesting_idxs = BTreeMap::new(); - for (field_def_idx, field_def) in self.module.field_defs.iter().enumerate() { - interesting_idxs - .entry(field_def.signature) - .or_insert_with(|| vec![]) - .push(field_def_idx); + pub fn apply(self) -> bool { + if self.module.struct_defs.is_empty() { + return false; } - // Convert into a Vec of pairs to allow pick_slice_idxs return vvalues to work. - let interesting_idxs: Vec<_> = interesting_idxs.into_iter().collect(); - - let picked = pick_slice_idxs(interesting_idxs.len(), &self.mutations); - let mut errs = vec![]; - for (mutation, picked_idx) in self.mutations.iter().zip(picked) { - let (type_sig_idx, field_def_idxs) = &interesting_idxs[picked_idx]; - let token = &mut self.module.type_signatures[type_sig_idx.into_index()].0; - let (new_token, token_kind) = if mutation.is_mutable { - ( - SignatureToken::MutableReference(Box::new(token.clone())), - SignatureTokenKind::MutableReference, - ) - } else { - ( - SignatureToken::Reference(Box::new(token.clone())), - SignatureTokenKind::Reference, - ) - }; - - *token = new_token; - - let msg = format!("with token {:#?} of kind {}", token.clone(), token_kind); - let violation = VMStatus::new(StatusCode::INVALID_FIELD_DEF).with_message(msg); - errs.extend(field_def_idxs.iter().map(|field_def_idx| { - append_err_info( - violation.clone(), - IndexKind::FieldDefinition, - *field_def_idx, - ) - })); + let module = self.module; + let mut mutations = false; + for (s_idx, f_idx) in self.mutations { + let struct_idx = s_idx.index(module.struct_defs.len()); + let struct_def = &mut module.struct_defs[struct_idx]; + if let StructFieldInformation::Declared(fields) = &mut struct_def.field_information { + if fields.is_empty() { + continue; + } + let field_idx = f_idx.index(fields.len()); + let field_def = &mut fields[field_idx]; + let new_ty = mutate_field(&field_def.signature.0); + fields[field_idx].signature = TypeSignature(new_ty); + mutations = true; + } } - - errs + mutations } } -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum SignatureIndex { - Type, - FunctionReturn(usize), - FunctionArg(usize), - Locals(usize), -} - -#[derive(Clone, Debug)] -struct DoubleRefMutationKind { - outer: SignatureTokenKind, - inner: SignatureTokenKind, +fn mutate_sig(sig: &Signature, token_idx: usize) -> Signature { + use SignatureToken::*; + + Signature( + sig.0 + .iter() + .enumerate() + .map(|(idx, token)| { + if idx == token_idx { + match &token { + Reference(_) | MutableReference(_) => Reference(Box::new(token.clone())), + _ => Reference(Box::new(Reference(Box::new(token.clone())))), + } + } else { + token.clone() + } + }) + .collect(), + ) } -impl DoubleRefMutationKind { - fn strategy() -> impl Strategy { - (Self::outer_strategy(), Self::inner_strategy()) - .prop_map(|(outer, inner)| Self { outer, inner }) - } - - fn wrap(&self, token: SignatureToken) -> SignatureToken { - let token = Self::wrap_one(token, self.inner); - Self::wrap_one(token, self.outer) - } - - fn wrap_one(token: SignatureToken, kind: SignatureTokenKind) -> SignatureToken { - match kind { - SignatureTokenKind::Reference => SignatureToken::Reference(Box::new(token)), - SignatureTokenKind::MutableReference => { - SignatureToken::MutableReference(Box::new(token)) - } - SignatureTokenKind::Value => panic!("invalid wrapping kind: {}", kind), - } - } - - #[inline] - fn outer_strategy() -> impl Strategy { - static VALID_OUTERS: &[SignatureTokenKind] = &[ - SignatureTokenKind::Reference, - SignatureTokenKind::MutableReference, - ]; - select(VALID_OUTERS) - } - - #[inline] - fn inner_strategy() -> impl Strategy { - static VALID_INNERS: &[SignatureTokenKind] = &[ - SignatureTokenKind::Reference, - SignatureTokenKind::MutableReference, - ]; - - select(VALID_INNERS) - } +fn mutate_field(token: &SignatureToken) -> SignatureToken { + SignatureToken::Reference(Box::new(token.clone())) } diff --git a/language/bytecode-verifier/src/abstract_state.rs b/language/bytecode-verifier/src/abstract_state.rs index a1412a859ee3..2826700cd8cd 100644 --- a/language/bytecode-verifier/src/abstract_state.rs +++ b/language/bytecode-verifier/src/abstract_state.rs @@ -2,16 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 //! This module defines the abstract state for the type and memory safety analysis. -use crate::absint::{AbstractDomain, JoinResult}; +use crate::{ + absint::{AbstractDomain, JoinResult}, + signature::kind, +}; use borrow_graph::references::RefID; use mirai_annotations::{checked_postcondition, checked_precondition, checked_verify}; use std::collections::{BTreeMap, BTreeSet}; use vm::{ + access::ModuleAccess, file_format::{ - CompiledModule, FieldDefinitionIndex, Kind, LocalIndex, SignatureToken, + CompiledModule, FieldHandleIndex, FunctionDefinition, Kind, LocalIndex, SignatureToken, StructDefinitionIndex, }, - views::{FunctionDefinitionView, ViewInternals}, }; type BorrowGraph = borrow_graph::graph::BorrowGraph<(), LabelElem>; @@ -22,6 +25,17 @@ pub struct TypedAbstractValue { pub value: AbstractValue, } +impl TypedAbstractValue { + pub fn reference_to(&self, type_sig: &SignatureToken) -> bool { + match &self.signature { + SignatureToken::Reference(ref_type) | SignatureToken::MutableReference(ref_type) => { + ref_type.as_ref() == type_sig + } + _ => false, + } + } +} + /// AbstractValue represents a value either on the evaluation stack or /// in a local on a frame of the function stack. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -48,7 +62,7 @@ impl AbstractValue { pub fn is_unrestricted_value(&self) -> bool { match self { AbstractValue::Reference(_) => false, - AbstractValue::Value(Kind::Unrestricted) => true, + AbstractValue::Value(Kind::Copyable) => true, AbstractValue::Value(Kind::All) | AbstractValue::Value(Kind::Resource) => false, } } @@ -57,7 +71,7 @@ impl AbstractValue { pub fn is_possibly_resource(&self) -> bool { match self { AbstractValue::Reference(_) => false, - AbstractValue::Value(Kind::Unrestricted) => false, + AbstractValue::Value(Kind::Copyable) => false, AbstractValue::Value(Kind::All) | AbstractValue::Value(Kind::Resource) => true, } } @@ -76,7 +90,7 @@ impl AbstractValue { pub enum LabelElem { Local(LocalIndex), Global(StructDefinitionIndex), - Field(FieldDefinitionIndex), + Field(FieldHandleIndex), } impl Default for LabelElem { @@ -96,34 +110,35 @@ pub struct AbstractState { impl AbstractState { /// create a new abstract state - pub fn new(function_definition_view: FunctionDefinitionView) -> Self { - let function_signature_view = function_definition_view.signature(); + pub fn new(module: &CompiledModule, function_definition: &FunctionDefinition) -> Self { let mut locals = BTreeMap::new(); let mut borrow_graph = BorrowGraph::new(); - for (arg_idx, arg_type_view) in function_signature_view.arg_tokens().enumerate() { - if arg_type_view.is_reference() { + + let func_handle = module.function_handle_at(function_definition.function); + let arguments = module.signature_at(func_handle.parameters); + for (arg_idx, argument) in arguments.0.iter().enumerate() { + if argument.is_reference() { let id = RefID::new(arg_idx); - borrow_graph.new_ref(id, arg_type_view.is_mutable_reference()); + borrow_graph.new_ref(id, argument.is_mutable_reference()); locals.insert( arg_idx as LocalIndex, TypedAbstractValue { - signature: arg_type_view.as_inner().clone(), + signature: argument.clone(), value: AbstractValue::Reference(id), }, ); } else { - let arg_kind = arg_type_view - .kind(&function_definition_view.signature().as_inner().type_formals); + let arg_kind = kind(module, argument, &func_handle.type_parameters); locals.insert( arg_idx as LocalIndex, TypedAbstractValue { - signature: arg_type_view.as_inner().clone(), + signature: argument.clone(), value: AbstractValue::Value(arg_kind), }, ); } } - let num_locals = function_definition_view.locals_signature().len(); + let num_locals = module.signature_at(function_definition.code.locals).len(); // ids in [0, num_locals] are reserved for constructing canonical state let next_id = num_locals + 1; let mut new_state = AbstractState { @@ -161,13 +176,13 @@ impl AbstractState { match self.locals[&idx].value { AbstractValue::Reference(_) => true, AbstractValue::Value(Kind::All) | AbstractValue::Value(Kind::Resource) => false, - AbstractValue::Value(Kind::Unrestricted) => !self.is_local_borrowed(idx), + AbstractValue::Value(Kind::Copyable) => !self.is_local_borrowed(idx), } } /// checks if the stack frame of the function being analyzed can be safely destroyed. /// safe destruction requires that all references in locals have already been destroyed - /// and all values in locals are unrestricted and unborrowed. + /// and all values in locals are copyable and unborrowed. pub fn is_frame_safe_to_destroy(&self) -> bool { self.locals .values() @@ -182,7 +197,7 @@ impl AbstractState { match local.value { AbstractValue::Reference(id) => self.release(id), AbstractValue::Value(kind) => { - checked_verify!(kind == Kind::Unrestricted); + checked_verify!(kind == Kind::Copyable); } } } @@ -270,7 +285,7 @@ impl AbstractState { &mut self, operand: &TypedAbstractValue, mut_: bool, - idx: FieldDefinitionIndex, + idx: FieldHandleIndex, ) -> Option { let id = operand.value.extract_id().unwrap(); if mut_ { @@ -420,7 +435,7 @@ impl AbstractState { // Join error, a resource is available along one path but not the other (None, Some(v)) | (Some(v), None) if v.value.is_possibly_resource() => return None, - // The local has a unrestricted value on one side but not the other, nothing to add + // The local has a copyable value on one side but not the other, nothing to add (Some(v), None) => { // A reference exists on one side, but not the other. Release if let AbstractValue::Reference(id) = &v.value { diff --git a/language/bytecode-verifier/src/acquires_list_verifier.rs b/language/bytecode-verifier/src/acquires_list_verifier.rs index cb0288062032..bf83a835ecec 100644 --- a/language/bytecode-verifier/src/acquires_list_verifier.rs +++ b/language/bytecode-verifier/src/acquires_list_verifier.rs @@ -15,7 +15,9 @@ use std::collections::BTreeSet; use vm::{ access::ModuleAccess, errors::err_at_offset, - file_format::{Bytecode, CompiledModule, FunctionDefinition, StructDefinitionIndex}, + file_format::{ + Bytecode, CompiledModule, FunctionDefinition, FunctionHandleIndex, StructDefinitionIndex, + }, views::{FunctionDefinitionView, ModuleView, StructDefinitionView, ViewInternals}, }; @@ -36,6 +38,7 @@ impl<'a> AcquiresVerifier<'a> { .iter() .cloned() .collect(); + let mut verifier = Self { module_view: ModuleView::new(module), annotated_acquires, @@ -69,34 +72,48 @@ impl<'a> AcquiresVerifier<'a> { fn verify_instruction(&mut self, instruction: &Bytecode, offset: usize) { match instruction { - Bytecode::Call(idx, _) => { - let function_handle = self.module_view.as_inner().function_handle_at(*idx); - let mut function_acquired_resources = self - .module_view - .function_acquired_resources(&function_handle); - for acquired_resource in &function_acquired_resources { - if !self.annotated_acquires.contains(acquired_resource) { - self.errors.push(err_at_offset( - StatusCode::MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR, - offset, - )) - } - } - self.actual_acquires - .append(&mut function_acquired_resources) + Bytecode::Call(idx) => self.call_acquire(*idx, offset), + Bytecode::CallGeneric(idx) => { + let fi = self.module_view.as_inner().function_instantiation_at(*idx); + self.call_acquire(fi.handle, offset) } - Bytecode::MoveFrom(idx, _) - | Bytecode::MutBorrowGlobal(idx, _) - | Bytecode::ImmBorrowGlobal(idx, _) => { - if !self.annotated_acquires.contains(idx) { - self.errors.push(err_at_offset( - StatusCode::MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR, - offset, - )) - } - self.actual_acquires.insert(*idx); + Bytecode::MoveFrom(idx) + | Bytecode::MutBorrowGlobal(idx) + | Bytecode::ImmBorrowGlobal(idx) => self.struct_acquire(*idx, offset), + Bytecode::MoveFromGeneric(idx) + | Bytecode::MutBorrowGlobalGeneric(idx) + | Bytecode::ImmBorrowGlobalGeneric(idx) => { + let si = self.module_view.as_inner().struct_instantiation_at(*idx); + self.struct_acquire(si.def, offset) } _ => (), } } + + fn call_acquire(&mut self, fh_idx: FunctionHandleIndex, offset: usize) { + let function_handle = self.module_view.as_inner().function_handle_at(fh_idx); + let mut function_acquired_resources = self + .module_view + .function_acquired_resources(function_handle); + for acquired_resource in &function_acquired_resources { + if !self.annotated_acquires.contains(acquired_resource) { + self.errors.push(err_at_offset( + StatusCode::MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR, + offset, + )) + } + } + self.actual_acquires + .append(&mut function_acquired_resources) + } + + fn struct_acquire(&mut self, sd_idx: StructDefinitionIndex, offset: usize) { + if !self.annotated_acquires.contains(&sd_idx) { + self.errors.push(err_at_offset( + StatusCode::MISSING_ACQUIRES_RESOURCE_ANNOTATION_ERROR, + offset, + )) + } + self.actual_acquires.insert(sd_idx); + } } diff --git a/language/bytecode-verifier/src/check_duplication.rs b/language/bytecode-verifier/src/check_duplication.rs index 6353cd70c4f7..5f747a19593c 100644 --- a/language/bytecode-verifier/src/check_duplication.rs +++ b/language/bytecode-verifier/src/check_duplication.rs @@ -14,8 +14,8 @@ use vm::{ access::ModuleAccess, errors::verification_error, file_format::{ - CompiledModule, FieldDefinitionIndex, FunctionHandleIndex, ModuleHandleIndex, - StructFieldInformation, StructHandleIndex, TableIndex, + CompiledModule, FunctionHandleIndex, ModuleHandleIndex, StructFieldInformation, + StructHandleIndex, }, IndexKind, }; @@ -32,6 +32,7 @@ impl<'a> DuplicationChecker<'a> { pub fn verify(self) -> Vec { let mut errors = vec![]; + // Identifiers if let Some(idx) = Self::first_duplicate_element(self.module.identifiers()) { errors.push(verification_error( IndexKind::Identifier, @@ -39,6 +40,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // Bytearrays if let Some(idx) = Self::first_duplicate_element(self.module.byte_array_pool()) { errors.push(verification_error( IndexKind::ByteArrayPool, @@ -46,6 +48,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // Addresses if let Some(idx) = Self::first_duplicate_element(self.module.address_pool()) { errors.push(verification_error( IndexKind::AddressPool, @@ -53,27 +56,15 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } - if let Some(idx) = Self::first_duplicate_element(self.module.type_signatures()) { + // Signatures + if let Some(idx) = Self::first_duplicate_element(self.module.signatures()) { errors.push(verification_error( - IndexKind::TypeSignature, - idx, - StatusCode::DUPLICATE_ELEMENT, - )) - } - if let Some(idx) = Self::first_duplicate_element(self.module.function_signatures()) { - errors.push(verification_error( - IndexKind::FunctionSignature, - idx, - StatusCode::DUPLICATE_ELEMENT, - )) - } - if let Some(idx) = Self::first_duplicate_element(self.module.locals_signatures()) { - errors.push(verification_error( - IndexKind::LocalsSignature, + IndexKind::Signature, idx, StatusCode::DUPLICATE_ELEMENT, )) } + // ModuleHandles if let Some(idx) = Self::first_duplicate_element(self.module.module_handles()) { errors.push(verification_error( IndexKind::ModuleHandle, @@ -81,6 +72,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // StructHandles - module and name define uniqueness if let Some(idx) = Self::first_duplicate_element( self.module .struct_handles() @@ -93,6 +85,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // FunctionHandles - module and name define uniqueness if let Some(idx) = Self::first_duplicate_element( self.module .function_handles() @@ -105,6 +98,39 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // FieldHandles + if let Some(idx) = Self::first_duplicate_element(self.module.field_handles()) { + errors.push(verification_error( + IndexKind::FieldHandle, + idx, + StatusCode::DUPLICATE_ELEMENT, + )) + } + // StructInstantiations + if let Some(idx) = Self::first_duplicate_element(self.module.struct_instantiations()) { + errors.push(verification_error( + IndexKind::StructDefInstantiation, + idx, + StatusCode::DUPLICATE_ELEMENT, + )) + } + // FunctionInstantiations + if let Some(idx) = Self::first_duplicate_element(self.module.function_instantiations()) { + errors.push(verification_error( + IndexKind::FunctionInstantiation, + idx, + StatusCode::DUPLICATE_ELEMENT, + )) + } + // FieldInstantiations + if let Some(idx) = Self::first_duplicate_element(self.module.field_instantiations()) { + errors.push(verification_error( + IndexKind::FieldInstantiation, + idx, + StatusCode::DUPLICATE_ELEMENT, + )) + } + // StructDefinition - contained StructHandle defines uniqueness if let Some(idx) = Self::first_duplicate_element(self.module.struct_defs().iter().map(|x| x.struct_handle)) { @@ -114,6 +140,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // FunctionDefinition - contained FunctionHandle defines uniqueness if let Some(idx) = Self::first_duplicate_element(self.module.function_defs().iter().map(|x| x.function)) { @@ -123,6 +150,7 @@ impl<'a> DuplicationChecker<'a> { StatusCode::DUPLICATE_ELEMENT, )) } + // Acquires in function declarations contain unique struct definitions for (idx, function_def) in self.module.function_defs().iter().enumerate() { let acquires = function_def.acquires_global_resources.iter(); if Self::first_duplicate_element(acquires).is_some() { @@ -133,66 +161,29 @@ impl<'a> DuplicationChecker<'a> { )) } } - if let Some(idx) = Self::first_duplicate_element( - self.module.field_defs().iter().map(|x| (x.struct_, x.name)), - ) { - errors.push(verification_error( - IndexKind::FieldDefinition, - idx, - StatusCode::DUPLICATE_ELEMENT, - )) - } - - // Check that: - // (1) the order of struct definitions matches the order of field definitions, - // (2) each struct definition and its field definitions point to the same struct handle, - // (3) there are no unused fields, - // (4) each struct has at least one field. serializing a struct with zero fields is problematic - let mut start_field_index: usize = 0; - let mut idx_opt = None; - for (idx, struct_def) in self.module.struct_defs().iter().enumerate() { - let (field_count, fields) = match struct_def.field_information { + // Field names in structs must be unique + for (struct_idx, struct_def) in self.module.struct_defs().iter().enumerate() { + let fields = match &struct_def.field_information { StructFieldInformation::Native => continue, - StructFieldInformation::Declared { - field_count, - fields, - } => (field_count, fields), + StructFieldInformation::Declared(fields) => fields, }; - if field_count == 0 { + if fields.is_empty() { errors.push(verification_error( IndexKind::StructDefinition, - idx, + struct_idx, StatusCode::ZERO_SIZED_STRUCT, )); } - if FieldDefinitionIndex::new(start_field_index as u16) != fields { - idx_opt = Some(idx); - break; - } - let next_start_field_index = start_field_index + field_count as usize; - let all_fields_match = (start_field_index..next_start_field_index).all(|i| { - struct_def.struct_handle - == self - .module - .field_def_at(FieldDefinitionIndex::new(i as TableIndex)) - .struct_ - }); - if !all_fields_match { - idx_opt = Some(idx); - break; + if let Some(idx) = Self::first_duplicate_element(fields.iter().map(|x| x.name)) { + errors.push(verification_error( + IndexKind::FieldDefinition, + idx, + StatusCode::DUPLICATE_ELEMENT, + )) } - start_field_index = next_start_field_index; - } - if let Some(idx) = idx_opt { - errors.push(verification_error( - IndexKind::StructDefinition, - idx, - StatusCode::INCONSISTENT_FIELDS, - )); } - - // Check that each struct definition is pointing to module handle with index - // IMPLEMENTED_MODULE_INDEX. + // Check that each struct definition is pointing to the self module (handle with index + // IMPLEMENTED_MODULE_INDEX) if let Some(idx) = self.module.struct_defs().iter().position(|x| { self.module.struct_handle_at(x.struct_handle).module != ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX) @@ -203,8 +194,8 @@ impl<'a> DuplicationChecker<'a> { StatusCode::INVALID_MODULE_HANDLE, )) } - // Check that each function definition is pointing to module handle with index - // IMPLEMENTED_MODULE_INDEX. + // Check that each function definition is pointing to the self module (handle with index + // IMPLEMENTED_MODULE_INDEX) if let Some(idx) = self.module.function_defs().iter().position(|x| { self.module.function_handle_at(x.function).module != ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX) @@ -215,8 +206,8 @@ impl<'a> DuplicationChecker<'a> { StatusCode::INVALID_MODULE_HANDLE, )) } - // Check that each struct handle with module handle index IMPLEMENTED_MODULE_INDEX is - // implemented. + // Check that each struct handle in self module (handle index IMPLEMENTED_MODULE_INDEX) is + // implemented (has a declaration) let implemented_struct_handles: HashSet = self .module .struct_defs() @@ -235,8 +226,8 @@ impl<'a> DuplicationChecker<'a> { StatusCode::UNIMPLEMENTED_HANDLE, )) } - // Check that each function handle with module handle index IMPLEMENTED_MODULE_INDEX is - // implemented. + // Check that each function handle in self module (handle index IMPLEMENTED_MODULE_INDEX) is + // implemented (has a declaration) let implemented_function_handles: HashSet = self .module .function_defs() diff --git a/language/bytecode-verifier/src/instantiation_loops.rs b/language/bytecode-verifier/src/instantiation_loops.rs index fb4de81a6701..04356a53c4ab 100644 --- a/language/bytecode-verifier/src/instantiation_loops.rs +++ b/language/bytecode-verifier/src/instantiation_loops.rs @@ -24,7 +24,7 @@ use vm::{ access::ModuleAccess, file_format::{ Bytecode, CompiledModule, FunctionDefinition, FunctionDefinitionIndex, FunctionHandleIndex, - LocalsSignatureIndex, SignatureToken, TypeParameterIndex, + SignatureIndex, SignatureToken, TypeParameterIndex, }, }; @@ -98,20 +98,20 @@ impl<'a> InstantiationLoopChecker<'a> { /// Helper function that extracts type parameters from a given type. /// Duplicated entries are removed. - fn extract_type_parameters(ty: &SignatureToken) -> HashSet { + fn extract_type_parameters(&self, ty: &SignatureToken) -> HashSet { use SignatureToken::*; let mut type_params = HashSet::new(); fn rec(type_params: &mut HashSet, ty: &SignatureToken) { match ty { - Bool | Address | U8 | U64 | U128 => (), + Bool | Address | U8 | U64 | U128 | Struct(_) => (), TypeParameter(idx) => { type_params.insert(*idx); } Vector(ty) => rec(type_params, ty), Reference(ty) | MutableReference(ty) => rec(type_params, ty), - Struct(_, tys) => { + StructInstantiation(_, tys) => { for ty in tys { rec(type_params, ty); } @@ -137,9 +137,9 @@ impl<'a> InstantiationLoopChecker<'a> { &mut self, caller_idx: FunctionDefinitionIndex, callee_idx: FunctionDefinitionIndex, - type_actuals_idx: LocalsSignatureIndex, + type_actuals_idx: SignatureIndex, ) { - let type_actuals = &self.module.locals_signature_at(type_actuals_idx).0; + let type_actuals = &self.module.signature_at(type_actuals_idx).0; for (formal_idx, ty) in type_actuals.iter().enumerate() { let formal_idx = formal_idx as TypeParameterIndex; @@ -150,7 +150,7 @@ impl<'a> InstantiationLoopChecker<'a> { Edge::Identity, ), _ => { - for type_param in Self::extract_type_parameters(ty) { + for type_param in self.extract_type_parameters(ty) { self.add_edge( Node(caller_idx, type_param), Node(callee_idx, formal_idx), @@ -170,13 +170,14 @@ impl<'a> InstantiationLoopChecker<'a> { caller_def: &FunctionDefinition, ) { for instr in &caller_def.code.code { - if let Bytecode::Call(callee_handle_idx, type_actuals_idx) = instr { + if let Bytecode::CallGeneric(callee_inst_idx) = instr { // Get the id of the definition of the function being called. // Skip if the function is not defined in the current module, as we do not // have mutual recursions across module boundaries. - if let Some(callee_idx) = self.func_handle_def_map.get(&callee_handle_idx) { + let callee_si = self.module.function_instantiation_at(*callee_inst_idx); + if let Some(callee_idx) = self.func_handle_def_map.get(&callee_si.handle) { let callee_idx = *callee_idx; - self.build_graph_call(caller_idx, callee_idx, *type_actuals_idx) + self.build_graph_call(caller_idx, callee_idx, callee_si.type_parameters) } } } diff --git a/language/bytecode-verifier/src/lib.rs b/language/bytecode-verifier/src/lib.rs index fbd543437918..c99b3516351f 100644 --- a/language/bytecode-verifier/src/lib.rs +++ b/language/bytecode-verifier/src/lib.rs @@ -13,6 +13,7 @@ pub mod check_duplication; pub mod code_unit_verifier; pub mod control_flow_graph; pub mod instantiation_loops; +pub mod resolver; pub mod resources; pub mod signature; pub mod stack_usage_verifier; @@ -28,7 +29,6 @@ pub use resources::ResourceTransitiveChecker; pub use signature::SignatureChecker; pub use stack_usage_verifier::StackUsageVerifier; pub use struct_defs::RecursiveStructDefChecker; -pub use unused_entries::UnusedEntryChecker; pub use verifier::{ batch_verify_modules, verify_main_signature, verify_module_dependencies, verify_script_dependencies, VerifiedModule, VerifiedScript, diff --git a/language/vm/src/resolver.rs b/language/bytecode-verifier/src/resolver.rs similarity index 64% rename from language/vm/src/resolver.rs rename to language/bytecode-verifier/src/resolver.rs index 796bc68fe19a..05ca5bf912f7 100644 --- a/language/vm/src/resolver.rs +++ b/language/bytecode-verifier/src/resolver.rs @@ -3,19 +3,19 @@ //! This module implements a resolver for importing a SignatureToken defined in one module into //! another. This functionaliy is used in verify_module_dependencies and verify_script_dependencies. -use crate::{ - access::ModuleAccess, - file_format::{ - AddressPoolIndex, FunctionSignature, IdentifierIndex, ModuleHandle, ModuleHandleIndex, - SignatureToken, StructHandle, StructHandleIndex, - }, -}; use libra_types::{ account_address::AccountAddress, vm_error::{StatusCode, VMStatus}, }; use move_core_types::identifier::Identifier; use std::collections::BTreeMap; +use vm::{ + access::ModuleAccess, + file_format::{ + AddressPoolIndex, IdentifierIndex, ModuleHandle, ModuleHandleIndex, Signature, + SignatureToken, StructHandle, StructHandleIndex, + }, +}; /// Resolution context for importing types pub struct Resolver { @@ -69,7 +69,7 @@ impl Resolver { SignatureToken::Vector(ty) => Ok(SignatureToken::Vector(Box::new( self.import_signature_token(dependency, ty)?, ))), - SignatureToken::Struct(sh_idx, types) => { + SignatureToken::Struct(sh_idx) => { let struct_handle = dependency.struct_handle_at(*sh_idx); let defining_module_handle = dependency.module_handle_at(struct_handle.module); let defining_module_address = dependency.address_at(defining_module_handle.address); @@ -95,14 +95,49 @@ impl Resolver { .get(struct_name) .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, is_nominal_resource: struct_handle.is_nominal_resource, - type_formals: struct_handle.type_formals.clone(), + type_parameters: struct_handle.type_parameters.clone(), }; Ok(SignatureToken::Struct( *self .struct_handle_map .get(&local_struct_handle) .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, - types + )) + } + SignatureToken::StructInstantiation(sh_idx, type_args) => { + let struct_handle = dependency.struct_handle_at(*sh_idx); + let defining_module_handle = dependency.module_handle_at(struct_handle.module); + let defining_module_address = dependency.address_at(defining_module_handle.address); + let defining_module_name = dependency.identifier_at(defining_module_handle.name); + let local_module_handle = ModuleHandle { + address: *self + .address_map + .get(defining_module_address) + .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, + name: *self + .identifier_map + .get(defining_module_name) + .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, + }; + let struct_name = dependency.identifier_at(struct_handle.name); + let local_struct_handle = StructHandle { + module: *self + .module_handle_map + .get(&local_module_handle) + .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, + name: *self + .identifier_map + .get(struct_name) + .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, + is_nominal_resource: struct_handle.is_nominal_resource, + type_parameters: struct_handle.type_parameters.clone(), + }; + Ok(SignatureToken::StructInstantiation( + *self + .struct_handle_map + .get(&local_struct_handle) + .ok_or_else(|| VMStatus::new(StatusCode::TYPE_RESOLUTION_FAILURE))?, + type_args .iter() .map(|t| self.import_signature_token(dependency, &t)) .collect::, VMStatus>>()?, @@ -119,25 +154,29 @@ impl Resolver { } } - /// given a function signature in dependency, construct an equivalent function signature in the - /// context of this resolver and return it; return an error if resolution fails - pub fn import_function_signature( + pub fn import_signature( &self, + signature: &Signature, dependency: &impl ModuleAccess, - func_sig: &FunctionSignature, - ) -> Result { - let mut return_types = Vec::::new(); - let mut arg_types = Vec::::new(); - for e in &func_sig.return_types { - return_types.push(self.import_signature_token(dependency, e)?); + ) -> Result { + let mut imported_signature = Vec::::new(); + for e in &signature.0 { + imported_signature.push(self.import_signature_token(dependency, e)?); } - for e in &func_sig.arg_types { - arg_types.push(self.import_signature_token(dependency, e)?); + Ok(Signature(imported_signature)) + } + + pub fn compare_cross_module_signatures( + &self, + handle_sig: &Signature, + def_sig: &Signature, + dependency: &impl ModuleAccess, + ) -> Result<(), VMStatus> { + let imported_signature = self.import_signature(def_sig, dependency)?; + if &imported_signature == handle_sig { + Ok(()) + } else { + Err(VMStatus::new(StatusCode::TYPE_MISMATCH)) } - Ok(FunctionSignature { - return_types, - arg_types, - type_formals: func_sig.type_formals.clone(), - }) } } diff --git a/language/bytecode-verifier/src/resources.rs b/language/bytecode-verifier/src/resources.rs index 7701308a40b6..262c9b2e4e2f 100644 --- a/language/bytecode-verifier/src/resources.rs +++ b/language/bytecode-verifier/src/resources.rs @@ -4,37 +4,41 @@ //! This module implements a checker for verifying that a non-resource struct does not //! have resource fields inside it. use libra_types::vm_error::{StatusCode, VMStatus}; -use vm::{errors::verification_error, file_format::CompiledModule, views::ModuleView, IndexKind}; +use vm::{ + access::ModuleAccess, + errors::verification_error, + file_format::{CompiledModule, Kind, SignatureToken, StructFieldInformation}, + IndexKind, +}; pub struct ResourceTransitiveChecker<'a> { - module_view: ModuleView<'a, CompiledModule>, + module: &'a CompiledModule, } impl<'a> ResourceTransitiveChecker<'a> { pub fn new(module: &'a CompiledModule) -> Self { - Self { - module_view: ModuleView::new(module), - } + Self { module } } pub fn verify(self) -> Vec { let mut errors = vec![]; - for (idx, struct_def) in self.module_view.structs().enumerate() { - if !struct_def.is_nominal_resource() { - match struct_def.fields() { - None => (), - Some(mut fields) => { - let any_resource_field = fields.any(|field| { - field - .type_signature() - .contains_nominal_resource(struct_def.type_formals()) - }); - if any_resource_field { - errors.push(verification_error( - IndexKind::StructDefinition, - idx, - StatusCode::INVALID_RESOURCE_FIELD, - )); + for (idx, struct_def) in self.module.struct_defs().iter().enumerate() { + let sh = self.module.struct_handle_at(struct_def.struct_handle); + if !sh.is_nominal_resource { + match &struct_def.field_information { + StructFieldInformation::Native => (), + StructFieldInformation::Declared(fields) => { + for field in fields { + if self + .contains_nominal_resource(&field.signature.0, &sh.type_parameters) + { + errors.push(verification_error( + IndexKind::StructDefinition, + idx, + StatusCode::INVALID_RESOURCE_FIELD, + )); + break; + } } } } @@ -42,4 +46,41 @@ impl<'a> ResourceTransitiveChecker<'a> { } errors } + + /// Determines if the given signature token contains a nominal resource. + /// More specifically, a signature token contains a nominal resource if + /// 1) it is a type variable explicitly marked as resource kind. + /// 2) it is a struct that + /// a) is marked as resource. + /// b) has a type actual which is a nominal resource. + fn contains_nominal_resource(&self, token: &SignatureToken, type_formals: &[Kind]) -> bool { + match token { + SignatureToken::Struct(sh_idx) => { + let sh = self.module.struct_handle_at(*sh_idx); + sh.is_nominal_resource + } + SignatureToken::StructInstantiation(sh_idx, type_params) => { + let sh = self.module.struct_handle_at(*sh_idx); + if sh.is_nominal_resource { + return true; + } + // TODO: not sure this is correct and comprehensive, review... + for token in type_params { + if self.contains_nominal_resource(token, type_formals) { + return true; + } + } + false + } + SignatureToken::Vector(ty) => self.contains_nominal_resource(ty, type_formals), + SignatureToken::Reference(_) + | SignatureToken::MutableReference(_) + | SignatureToken::Bool + | SignatureToken::U8 + | SignatureToken::U64 + | SignatureToken::U128 + | SignatureToken::Address + | SignatureToken::TypeParameter(_) => false, + } + } } diff --git a/language/bytecode-verifier/src/signature.rs b/language/bytecode-verifier/src/signature.rs index bef273221ed8..bd07ceca9698 100644 --- a/language/bytecode-verifier/src/signature.rs +++ b/language/bytecode-verifier/src/signature.rs @@ -8,10 +8,7 @@ use libra_types::vm_error::{StatusCode, VMStatus}; use vm::{ access::ModuleAccess, errors::append_err_info, - file_format::{ - Bytecode, CompiledModule, Kind, SignatureToken, StructFieldInformation, StructHandle, - TypeSignature, - }, + file_format::{Bytecode, CompiledModule, Kind, SignatureToken, StructFieldInformation}, IndexKind, }; @@ -24,51 +21,61 @@ impl<'a> SignatureChecker<'a> { Self { module } } + // TODO: should probably verify the Signature pool in general pub fn verify(self) -> Vec { - self.verify_function_signatures() + self.verify_signatures() + .chain(self.verify_function_signatures()) .chain(self.verify_fields()) .chain(self.verify_code_units()) - .chain(self.legacy_verify_type_signatures()) .collect() } - /// This is a hack to satisfy the existing prop tests. - /// TODO: Remove it once we rework prop tests. - fn legacy_verify_type_signatures(&self) -> impl Iterator + '_ { + fn verify_signatures(&self) -> impl Iterator + '_ { use SignatureToken::*; - self.module - .type_signatures() - .iter() - .filter_map(|TypeSignature(ty)| match ty { - Reference(inner) | MutableReference(inner) => match **inner { - Reference(_) | MutableReference(_) => { - Some(VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN)) - } - _ => None, - }, - _ => None, - }) + self.module.signatures().iter().filter_map(|sig| { + for ty in &sig.0 { + match ty { + Reference(inner) | MutableReference(inner) => match **inner { + Reference(_) | MutableReference(_) => { + return Some(VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN)); + } + _ => (), + }, + Vector(inner) => match **inner { + Reference(_) | MutableReference(_) => { + return Some(VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN)); + } + _ => (), // TODO: vector of vectors + }, + _ => (), + } + } + None + }) } fn verify_function_signatures(&self) -> impl Iterator + '_ { self.module - .function_signatures() + .function_handles() .iter() .enumerate() - .flat_map(move |(idx, sig)| { - let context = (self.module.struct_handles(), sig.type_formals.as_slice()); - let errors_return_types = sig.return_types.iter().flat_map(move |ty| { - check_signature(context, ty) + .flat_map(move |(idx, fh)| { + let type_parameters = fh.type_parameters.as_slice(); + + let return_ = self.module.signature_at(fh.return_); + let errors_return = return_.0.iter().flat_map(move |ty| { + self.check_signature(ty, type_parameters) .into_iter() - .map(move |err| append_err_info(err, IndexKind::FunctionSignature, idx)) + .map(move |err| append_err_info(err, IndexKind::Signature, idx)) }); - let errors_arg_types = sig.arg_types.iter().flat_map(move |ty| { - check_signature(context, ty) + let parameters = self.module.signature_at(fh.parameters); + let errors_params = parameters.0.iter().flat_map(move |ty| { + self.check_signature(ty, type_parameters) .into_iter() - .map(move |err| append_err_info(err, IndexKind::FunctionSignature, idx)) + .map(move |err| append_err_info(err, IndexKind::Signature, idx)) }); - errors_return_types.chain(errors_arg_types) + errors_return.chain(errors_params) }) } @@ -78,45 +85,39 @@ impl<'a> SignatureChecker<'a> { .iter() .enumerate() .filter_map( - move |(struct_def_idx, struct_def)| match struct_def.field_information { + move |(struct_def_idx, struct_def)| match &struct_def.field_information { StructFieldInformation::Native => None, - StructFieldInformation::Declared { - field_count, - fields, - } => { + StructFieldInformation::Declared(fields) => { let struct_handle = self.module.struct_handle_at(struct_def.struct_handle); - let start = fields.0 as usize; - let end = start + (field_count as usize); - let context = ( - self.module.struct_handles(), - struct_handle.type_formals.as_slice(), - ); + let type_parameters = &struct_handle.type_parameters; - let errors = self.module.field_defs()[start..end] - .iter() - .enumerate() - .flat_map(move |(field_def_idx, field_def)| { - let ty = self.module.type_signature_at(field_def.signature); - - check_signature_no_refs(context, &ty.0).into_iter().map( - move |err| { - append_err_info( + let errors = + fields + .iter() + .enumerate() + .flat_map(move |(field_offset, field_def)| { + let ty = &field_def.signature; + self.check_signature_no_refs(&ty.0, type_parameters) + .into_iter() + .map(move |err| { append_err_info( append_err_info( - VMStatus::new(StatusCode::INVALID_FIELD_DEF) + append_err_info( + VMStatus::new( + StatusCode::INVALID_FIELD_DEF, + ) .append(err), - IndexKind::TypeSignature, - field_def.signature.0 as usize, + IndexKind::FieldDefinition, + field_offset, + ), + IndexKind::FieldDefinition, + field_offset, ), - IndexKind::FieldDefinition, - field_def_idx, - ), - IndexKind::StructDefinition, - struct_def_idx, - ) - }, - ) - }); + IndexKind::StructDefinition, + struct_def_idx, + ) + }) + }); Some(errors) } }, @@ -139,21 +140,20 @@ impl<'a> SignatureChecker<'a> { // Check if the types of the locals are well defined. let func_handle = self.module.function_handle_at(func_def.function); - let func_sig = self.module.function_signature_at(func_handle.signature); - let context = ( - self.module.struct_handles(), - func_sig.type_formals.as_slice(), - ); + let type_parameters = func_handle.type_parameters.as_slice(); + let locals_idx = func_def.code.locals; - let locals = &self.module.locals_signature_at(locals_idx).0; + let locals = &self.module.signature_at(locals_idx).0; let errors_locals = locals.iter().flat_map(move |ty| { - check_signature(context, ty).into_iter().map(move |err| { - append_err_info( - append_err_info(err, IndexKind::LocalsSignature, locals_idx.0 as usize), - IndexKind::FunctionDefinition, - func_def_idx, - ) - }) + self.check_signature(ty, type_parameters) + .into_iter() + .map(move |err| { + append_err_info( + append_err_info(err, IndexKind::Signature, locals_idx.0 as usize), + IndexKind::FunctionDefinition, + func_def_idx, + ) + }) }); // Check if the type actuals in certain bytecode instructions are well defined. @@ -165,44 +165,38 @@ impl<'a> SignatureChecker<'a> { .enumerate() .flat_map(move |(offset, instr)| { let errors = match instr { - Call(idx, type_actuals_idx) => { - let func_handle = self.module.function_handle_at(*idx); - let func_sig = - self.module.function_signature_at(func_handle.signature); - let type_actuals = - &self.module.locals_signature_at(*type_actuals_idx).0; - check_generic_instance( - context, - &func_sig.type_formals, - type_actuals, - ) - } - Pack(idx, type_actuals_idx) | Unpack(idx, type_actuals_idx) => { - let struct_def = self.module.struct_def_at(*idx); - let struct_handle = - self.module.struct_handle_at(struct_def.struct_handle); - let type_actuals = - &self.module.locals_signature_at(*type_actuals_idx).0; - check_generic_instance( - context, - &struct_handle.type_formals, - type_actuals, + CallGeneric(idx) => { + let func_inst = self.module.function_instantiation_at(*idx); + let func_handle = + self.module.function_handle_at(func_inst.handle); + let type_arguments = self + .module + .signature_at(func_inst.type_parameters) + .0 + .as_slice(); + self.check_generic_instance( + type_arguments, + &func_handle.type_parameters, + type_parameters, ) } - Exists(idx, type_actuals_idx) - | MoveFrom(idx, type_actuals_idx) - | MoveToSender(idx, type_actuals_idx) - | ImmBorrowGlobal(idx, type_actuals_idx) - | MutBorrowGlobal(idx, type_actuals_idx) => { - let struct_def = self.module.struct_def_at(*idx); + PackGeneric(idx) + | UnpackGeneric(idx) + | ExistsGeneric(idx) + | MoveFromGeneric(idx) + | MoveToSenderGeneric(idx) + | ImmBorrowGlobalGeneric(idx) + | MutBorrowGlobalGeneric(idx) => { + let struct_inst = self.module.struct_instantiation_at(*idx); + let struct_def = self.module.struct_def_at(struct_inst.def); let struct_handle = self.module.struct_handle_at(struct_def.struct_handle); - let type_actuals = - &self.module.locals_signature_at(*type_actuals_idx).0; - check_generic_instance( - context, - &struct_handle.type_formals, - type_actuals, + let type_arguments = + &self.module.signature_at(struct_inst.type_parameters).0; + self.check_generic_instance( + type_arguments, + struct_handle.type_parameters.as_slice(), + type_parameters, ) } _ => vec![], @@ -223,86 +217,116 @@ impl<'a> SignatureChecker<'a> { }) .flatten() } -} -// Checks if the given types are well defined and satisfy the given kind constraints in the given -// context. -fn check_generic_instance( - context: (&[StructHandle], &[Kind]), - constraints: &[Kind], - type_actuals: &[SignatureToken], -) -> Vec { - let mut errors: Vec<_> = type_actuals - .iter() - .flat_map(|ty| check_signature_no_refs(context, ty)) - .collect(); + // Checks if the given types are well defined and satisfy the given kind constraints in + // the given context. + fn check_generic_instance( + &self, + type_arguments: &[SignatureToken], + scope_kinds: &[Kind], + global_kinds: &[Kind], + ) -> Vec { + let mut errors: Vec<_> = type_arguments + .iter() + .flat_map(|ty| self.check_signature_no_refs(ty, global_kinds)) + .collect(); - if constraints.len() != type_actuals.len() { - errors.push( - VMStatus::new(StatusCode::NUMBER_OF_TYPE_ACTUALS_MISMATCH).with_message(format!( - "expected {} type actuals got {}", - constraints.len(), - type_actuals.len() - )), + let kinds = type_arguments + .iter() + .map(|ty| kind(self.module, ty, global_kinds)); + errors.extend( + scope_kinds + .iter() + .zip(kinds) + .zip(type_arguments.iter()) + .filter_map(|((c, k), ty)| { + if k.is_sub_kind_of(*c) { + return None; + } + Some( + VMStatus::new(StatusCode::CONTRAINT_KIND_MISMATCH).with_message(format!( + "expected kind {:?} got type actual {:?} with incompatible kind {:?}", + c, ty, k + )), + ) + }), ); - return errors; + errors } - let kinds = type_actuals - .iter() - .map(|ty| SignatureToken::kind(context, ty)); - errors.extend( - constraints - .iter() - .zip(kinds) - .zip(type_actuals.iter()) - .filter_map(|((c, k), ty)| { - if k.is_sub_kind_of(*c) { - return None; - } - Some( - VMStatus::new(StatusCode::CONTRAINT_KIND_MISMATCH).with_message(format!( - "expected kind {:?} got type actual {:?} with incompatible kind {:?}", - c, ty, k - )), - ) - }), - ); - errors -} + /// Checks if the given type is well defined in the given context. No references are permitted. + fn check_signature_no_refs(&self, ty: &SignatureToken, kinds: &[Kind]) -> Vec { + use SignatureToken::*; -/// Checks if the given type is well defined in the given context. No references are permitted. -fn check_signature_no_refs( - context: (&[StructHandle], &[Kind]), - ty: &SignatureToken, -) -> Vec { - use SignatureToken::*; + match ty { + U8 | U64 | U128 | Bool | Address | Struct(_) | TypeParameter(_) => vec![], + Reference(_) | MutableReference(_) => { + // TODO: Prop tests expect us to NOT check the inner types. + // Revisit this once we rework prop tests. + vec![VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN) + .with_message("reference not allowed".to_string())] + } + Vector(ty) => self.check_signature_no_refs(ty, kinds), + StructInstantiation(idx, type_arguments) => { + let sh = self.module.struct_handle_at(*idx); + self.check_generic_instance(type_arguments, &sh.type_parameters, kinds) + } + } + } - let (struct_handles, _) = context; + /// Checks if the given type is well defined in the given context. References are only permitted + /// at the top level. + fn check_signature(&self, ty: &SignatureToken, kinds: &[Kind]) -> Vec { + use SignatureToken::*; - match ty { - U8 | U64 | U128 | Bool | Address | TypeParameter(_) => vec![], - Reference(_) | MutableReference(_) => { - // TODO: Prop tests expect us to NOT check the inner types. - // Revisit this once we rework prop tests. - vec![VMStatus::new(StatusCode::INVALID_SIGNATURE_TOKEN) - .with_message("reference not allowed".to_string())] - } - Vector(ty) => check_signature_no_refs(context, ty), - Struct(idx, type_actuals) => { - let sh = &struct_handles[idx.0 as usize]; - check_generic_instance(context, &sh.type_formals, type_actuals) + match ty { + Reference(inner) | MutableReference(inner) => { + self.check_signature_no_refs(inner, kinds) + } + _ => self.check_signature_no_refs(ty, kinds), } } } -/// Checks if the given type is well defined in the given context. References are only permitted -/// at the top level. -fn check_signature(context: (&[StructHandle], &[Kind]), ty: &SignatureToken) -> Vec { +// +// Helpers functions for signatures +// + +pub(crate) fn kind(module: &CompiledModule, ty: &SignatureToken, constraints: &[Kind]) -> Kind { use SignatureToken::*; match ty { - Reference(inner) | MutableReference(inner) => check_signature_no_refs(context, inner), - _ => check_signature_no_refs(context, ty), + // The primitive types & references have kind unrestricted. + Bool | U8 | U64 | U128 | Address | Reference(_) | MutableReference(_) => Kind::Copyable, + TypeParameter(idx) => constraints[*idx as usize], + Vector(ty) => kind(module, ty, constraints), + Struct(idx) => { + let sh = module.struct_handle_at(*idx); + if sh.is_nominal_resource { + Kind::Resource + } else { + Kind::Copyable + } + } + StructInstantiation(idx, type_args) => { + let sh = module.struct_handle_at(*idx); + if sh.is_nominal_resource { + return Kind::Resource; + } + // Gather the kinds of the type actuals. + let kinds = type_args + .iter() + .map(|ty| kind(module, ty, constraints)) + .collect::>(); + // Derive the kind of the struct. + // - If any of the type actuals is `all`, then the struct is `all`. + // - `all` means some part of the type can be either `resource` or + // `unrestricted`. + // - Therefore it is also impossible to determine the kind of the type as a + // whole, and thus `all`. + // - If none of the type actuals is `all`, then the struct is a resource if + // and only if one of the type actuals is `resource`. + kinds.iter().cloned().fold(Kind::Copyable, Kind::join) + } } } diff --git a/language/bytecode-verifier/src/stack_usage_verifier.rs b/language/bytecode-verifier/src/stack_usage_verifier.rs index 9bd177d730d5..053454438a6e 100644 --- a/language/bytecode-verifier/src/stack_usage_verifier.rs +++ b/language/bytecode-verifier/src/stack_usage_verifier.rs @@ -13,13 +13,15 @@ use libra_types::vm_error::{StatusCode, VMStatus}; use vm::{ access::ModuleAccess, errors::err_at_offset, - file_format::{Bytecode, CompiledModule, FunctionDefinition, StructFieldInformation}, - views::FunctionDefinitionView, + file_format::{ + Bytecode, CompiledModule, FunctionDefinition, FunctionHandle, StructFieldInformation, + }, }; pub struct StackUsageVerifier<'a> { module: &'a CompiledModule, - function_definition_view: FunctionDefinitionView<'a, CompiledModule>, + function_definition: &'a FunctionDefinition, + function_handle: &'a FunctionHandle, } impl<'a> StackUsageVerifier<'a> { @@ -28,10 +30,11 @@ impl<'a> StackUsageVerifier<'a> { function_definition: &'a FunctionDefinition, cfg: &'a VMControlFlowGraph, ) -> Vec { - let function_definition_view = FunctionDefinitionView::new(module, function_definition); + let function_handle = module.function_handle_at(function_definition.function); let verifier = Self { module, - function_definition_view, + function_definition, + function_handle, }; let mut errors = vec![]; @@ -42,7 +45,7 @@ impl<'a> StackUsageVerifier<'a> { } fn verify_block(&self, block_id: BlockId, cfg: &dyn ControlFlowGraph) -> Vec { - let code = &self.function_definition_view.code().code; + let code = &self.function_definition.code.code; let mut stack_size_increment = 0; let block_start = cfg.block_start(block_id); for i in block_start..=cfg.block_end(block_id) { @@ -79,7 +82,8 @@ impl<'a> StackUsageVerifier<'a> { | Bytecode::BrTrue(_) | Bytecode::BrFalse(_) | Bytecode::Abort - | Bytecode::MoveToSender(_, _) + | Bytecode::MoveToSender(_) + | Bytecode::MoveToSenderGeneric(_) | Bytecode::StLoc(_) => (1, 0), // Instructions that push, but don't pop @@ -105,12 +109,18 @@ impl<'a> StackUsageVerifier<'a> { Bytecode::Not | Bytecode::FreezeRef | Bytecode::ReadRef - | Bytecode::Exists(_, _) - | Bytecode::MutBorrowGlobal(_, _) - | Bytecode::ImmBorrowGlobal(_, _) + | Bytecode::Exists(_) + | Bytecode::ExistsGeneric(_) + | Bytecode::MutBorrowGlobal(_) + | Bytecode::MutBorrowGlobalGeneric(_) + | Bytecode::ImmBorrowGlobal(_) + | Bytecode::ImmBorrowGlobalGeneric(_) | Bytecode::MutBorrowField(_) + | Bytecode::MutBorrowFieldGeneric(_) | Bytecode::ImmBorrowField(_) - | Bytecode::MoveFrom(_, _) + | Bytecode::ImmBorrowFieldGeneric(_) + | Bytecode::MoveFrom(_) + | Bytecode::MoveFromGeneric(_) | Bytecode::CastU8 | Bytecode::CastU64 | Bytecode::CastU128 => (1, 1), @@ -143,41 +153,65 @@ impl<'a> StackUsageVerifier<'a> { // Return performs `return_count` pops Bytecode::Ret => { - let return_count = self.function_definition_view.signature().return_count() as u32; - (return_count, 0) + let return_count = self.module.signature_at(self.function_handle.return_).len(); + (return_count as u32, 0) } // Call performs `arg_count` pops and `return_count` pushes - Bytecode::Call(idx, _) => { + Bytecode::Call(idx) => { let function_handle = self.module.function_handle_at(*idx); - let signature = self.module.function_signature_at(function_handle.signature); - let arg_count = signature.arg_types.len() as u32; - let return_count = signature.return_types.len() as u32; + let arg_count = self.module.signature_at(function_handle.parameters).len() as u32; + let return_count = self.module.signature_at(function_handle.return_).len() as u32; + (arg_count, return_count) + } + Bytecode::CallGeneric(idx) => { + let func_inst = self.module.function_instantiation_at(*idx); + let function_handle = self.module.function_handle_at(func_inst.handle); + let arg_count = self.module.signature_at(function_handle.parameters).len() as u32; + let return_count = self.module.signature_at(function_handle.return_).len() as u32; (arg_count, return_count) } // Pack performs `num_fields` pops and one push - Bytecode::Pack(idx, _) => { + Bytecode::Pack(idx) => { let struct_definition = self.module.struct_def_at(*idx); let field_count = match &struct_definition.field_information { // 'Native' here is an error that will be caught by the bytecode verifier later StructFieldInformation::Native => 0, - StructFieldInformation::Declared { field_count, .. } => *field_count, + StructFieldInformation::Declared(fields) => fields.len(), + }; + (field_count as u32, 1) + } + Bytecode::PackGeneric(idx) => { + let struct_inst = self.module.struct_instantiation_at(*idx); + let struct_definition = self.module.struct_def_at(struct_inst.def); + let field_count = match &struct_definition.field_information { + // 'Native' here is an error that will be caught by the bytecode verifier later + StructFieldInformation::Native => 0, + StructFieldInformation::Declared(fields) => fields.len(), }; - let num_fields = u32::from(field_count); - (num_fields, 1) + (field_count as u32, 1) } // Unpack performs one pop and `num_fields` pushes - Bytecode::Unpack(idx, _) => { + Bytecode::Unpack(idx) => { let struct_definition = self.module.struct_def_at(*idx); let field_count = match &struct_definition.field_information { // 'Native' here is an error that will be caught by the bytecode verifier later StructFieldInformation::Native => 0, - StructFieldInformation::Declared { field_count, .. } => *field_count, + StructFieldInformation::Declared(fields) => fields.len(), + }; + (1, field_count as u32) + } + Bytecode::UnpackGeneric(idx) => { + let struct_inst = self.module.struct_instantiation_at(*idx); + let struct_definition = self.module.struct_def_at(struct_inst.def); + let field_count = match &struct_definition.field_information { + // 'Native' here is an error that will be caught by the bytecode verifier later + StructFieldInformation::Native => 0, + StructFieldInformation::Declared(fields) => fields.len(), }; - let num_fields = u32::from(field_count); - (1, num_fields) + (1, field_count as u32) } } } diff --git a/language/bytecode-verifier/src/struct_defs.rs b/language/bytecode-verifier/src/struct_defs.rs index f92d063e29b9..d0865e9adb0a 100644 --- a/language/bytecode-verifier/src/struct_defs.rs +++ b/language/bytecode-verifier/src/struct_defs.rs @@ -64,7 +64,7 @@ impl<'a> StructDefGraphBuilder<'a> { // DuplicationChecker for (idx, struct_def) in module.struct_defs().iter().enumerate() { let sh_idx = struct_def.struct_handle; - handle_to_def.insert(sh_idx, StructDefinitionIndex::new(idx as TableIndex)); + handle_to_def.insert(sh_idx, StructDefinitionIndex(idx as TableIndex)); } Self { @@ -115,7 +115,15 @@ impl<'a> StructDefGraphBuilder<'a> { .with_message("Reference field when checking recursive structs".to_owned())) } T::Vector(inner) => self.add_signature_token(neighbors, cur_idx, inner)?, - T::Struct(sh_idx, inners) => { + T::Struct(sh_idx) => { + if let Some(struct_def_idx) = self.handle_to_def.get(sh_idx) { + neighbors + .entry(cur_idx) + .or_insert_with(BTreeSet::new) + .insert(*struct_def_idx); + } + } + T::StructInstantiation(sh_idx, inners) => { if let Some(struct_def_idx) = self.handle_to_def.get(sh_idx) { neighbors .entry(cur_idx) diff --git a/language/bytecode-verifier/src/type_memory_safety.rs b/language/bytecode-verifier/src/type_memory_safety.rs index 79bb105490d5..450cb8e4fbd1 100644 --- a/language/bytecode-verifier/src/type_memory_safety.rs +++ b/language/bytecode-verifier/src/type_memory_safety.rs @@ -10,28 +10,30 @@ use crate::{ }, abstract_state::{AbstractState, AbstractValue, TypedAbstractValue}, control_flow_graph::VMControlFlowGraph, + signature::kind, }; use borrow_graph::references::RefID; use libra_types::vm_error::{StatusCode, VMStatus}; -use mirai_annotations::checked_verify; +use mirai_annotations::*; use std::collections::BTreeSet; use vm::{ access::ModuleAccess, errors::err_at_offset, file_format::{ - Bytecode, CompiledModule, FieldDefinitionIndex, FunctionDefinition, Kind, LocalIndex, - LocalsSignatureIndex, SignatureToken, StructDefinitionIndex, + Bytecode, CompiledModule, FieldHandleIndex, FunctionDefinition, FunctionHandle, Kind, + LocalIndex, Signature, SignatureToken, StructDefinition, StructDefinitionIndex, + StructFieldInformation, StructHandleIndex, }, views::{ - FunctionDefinitionView, FunctionSignatureView, LocalsSignatureView, ModuleView, - SignatureTokenView, StructDefinitionView, ViewInternals, + FunctionDefinitionView, FunctionHandleView, ModuleView, SignatureView, + StructDefinitionView, ViewInternals, }, }; pub struct TypeAndMemorySafetyAnalysis<'a> { module_view: ModuleView<'a, CompiledModule>, function_definition_view: FunctionDefinitionView<'a, CompiledModule>, - locals_signature_view: LocalsSignatureView<'a, CompiledModule>, + locals_signature_view: SignatureView<'a, CompiledModule>, stack: Vec, } @@ -43,16 +45,16 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { ) -> Vec { let function_definition_view = FunctionDefinitionView::new(module, function_definition); let locals_signature_view = function_definition_view.locals_signature(); - let function_signature_view = function_definition_view.signature(); - if function_signature_view.arg_count() > locals_signature_view.len() { + // TODO: this check should probably be elsewhere + if function_definition_view.arg_count() > locals_signature_view.len() { return vec![VMStatus::new(StatusCode::RANGE_OUT_OF_BOUNDS) .with_message("Fewer locals than parameters".to_string())]; } - let errors: Vec = function_signature_view + let errors: Vec = function_definition_view .arg_tokens() .enumerate() - .flat_map(|(arg_idx, arg_type_view)| { - let arg_token = arg_type_view.as_inner(); + .flat_map(|(arg_idx, parameter_view)| { + let arg_token = parameter_view.as_inner(); let local_token = locals_signature_view .token_at(arg_idx as LocalIndex) .as_inner(); @@ -71,8 +73,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if !errors.is_empty() { return errors; } - let initial_state = - AbstractState::new(FunctionDefinitionView::new(module, function_definition)); + let initial_state = AbstractState::new(module, function_definition); let mut verifier = Self { module_view: ModuleView::new(module), function_definition_view: FunctionDefinitionView::new(module, function_definition), @@ -106,12 +107,8 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { } /// Gives the current constraints on the type formals in the current function. - fn type_formals(&self) -> &[Kind] { - &self - .function_definition_view - .signature() - .as_inner() - .type_formals + fn type_parameters(&self) -> &[Kind] { + self.function_definition_view.type_parameters() } fn is_readable_reference(state: &AbstractState, signature: &SignatureToken, id: RefID) -> bool { @@ -126,31 +123,26 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { state: &mut AbstractState, offset: usize, mut_: bool, - field_definition_index: FieldDefinitionIndex, + field_handle_index: FieldHandleIndex, + type_args: &Signature, ) { + // load operand and check mutability constraints let operand = self.stack.pop().unwrap(); - let struct_handle_index = - match SignatureToken::get_struct_handle_from_reference(&operand.signature) { - Some(struct_handle_index) => struct_handle_index, - None => { - errors.push(err_at_offset( - StatusCode::BORROWFIELD_TYPE_MISMATCH_ERROR, - offset, - )); - return; - } - }; - if !self - .module() - .is_field_in_struct(field_definition_index, struct_handle_index) - { + if mut_ && !operand.signature.is_mutable_reference() { errors.push(err_at_offset( - StatusCode::BORROWFIELD_BAD_FIELD_ERROR, + StatusCode::BORROWFIELD_TYPE_MISMATCH_ERROR, offset, )); return; } - if mut_ && !operand.signature.is_mutable_reference() { + + // check the reference on the stack is the expected type. + // Load the type that owns the field according to the instruction. + // For generic fields access, this step materializes that type + let field_handle = self.module().field_handle_at(field_handle_index); + let struct_def = self.module().struct_def_at(field_handle.owner); + let expected_type = materialize_type(struct_def.struct_handle, type_args); + if !operand.reference_to(&expected_type) { errors.push(err_at_offset( StatusCode::BORROWFIELD_TYPE_MISMATCH_ERROR, offset, @@ -158,32 +150,41 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { return; } - if let Some(id) = state.borrow_field(&operand, mut_, field_definition_index) { - let field_signature = self - .module() - .get_field_signature(field_definition_index) - .0 - .clone(); - let field_token = Box::new( - field_signature - .substitute(operand.signature.get_type_actuals_from_reference().unwrap()), - ); - let signature = if mut_ { - SignatureToken::MutableReference(field_token) - } else { - SignatureToken::Reference(field_token) - }; - self.stack.push(TypedAbstractValue { - signature, - value: AbstractValue::Reference(id), - }); - let operand_id = operand.value.extract_id().unwrap(); - state.release(operand_id); - } else { - errors.push(err_at_offset( + match state.borrow_field(&operand, mut_, field_handle_index) { + Some(id) => { + // load the field type + let field_def = match &struct_def.field_information { + StructFieldInformation::Native => { + errors.push(err_at_offset( + StatusCode::BORROWFIELD_BAD_FIELD_ERROR, + offset, + )); + return; + } + StructFieldInformation::Declared(fields) => { + // TODO: review the whole error story here, way too much is left to chances... + // definition of a more proper OM for the verifier could work around the problem + // (maybe, maybe not..) + &fields[field_handle.field as usize] + } + }; + let field_type = Box::new(instantiate(&field_def.signature.0, type_args)); + let signature = if mut_ { + SignatureToken::MutableReference(field_type) + } else { + SignatureToken::Reference(field_type) + }; + self.stack.push(TypedAbstractValue { + signature, + value: AbstractValue::Reference(id), + }); + let operand_id = operand.value.extract_id().unwrap(); + state.release(operand_id); + } + None => errors.push(err_at_offset( StatusCode::BORROWFIELD_EXISTS_MUTABLE_BORROW_ERROR, offset, - )) + )), } } @@ -235,31 +236,30 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { offset: usize, mut_: bool, idx: StructDefinitionIndex, - type_actuals_idx: LocalsSignatureIndex, + type_args: &Signature, ) { - let struct_definition = self.module().struct_def_at(idx); - if !StructDefinitionView::new(self.module(), struct_definition).is_nominal_resource() { + // check and consume top of stack + let operand = self.stack.pop().unwrap(); + if operand.signature != SignatureToken::Address { errors.push(err_at_offset( - StatusCode::BORROWGLOBAL_NO_RESOURCE_ERROR, + StatusCode::BORROWGLOBAL_TYPE_MISMATCH_ERROR, offset, )); return; } - let type_actuals = &self.module().locals_signature_at(type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - SignatureTokenView::new(self.module(), &struct_type).kind(self.type_formals()); - let operand = self.stack.pop().unwrap(); - if operand.signature != SignatureToken::Address { + let struct_def = self.module().struct_def_at(idx); + let struct_handle = self.module().struct_handle_at(struct_def.struct_handle); + if !struct_handle.is_nominal_resource { errors.push(err_at_offset( - StatusCode::BORROWFIELD_TYPE_MISMATCH_ERROR, + StatusCode::BORROWGLOBAL_NO_RESOURCE_ERROR, offset, )); return; } if let Some(id) = state.borrow_global_value(mut_, idx) { + let struct_type = materialize_type(struct_def.struct_handle, type_args); let signature = if mut_ { SignatureToken::MutableReference(Box::new(struct_type)) } else { @@ -274,6 +274,233 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { } } + fn call( + &mut self, + errors: &mut Vec, + state: &mut AbstractState, + offset: usize, + function_handle: &FunctionHandle, + type_actuals: &Signature, + ) { + let function_acquired_resources = self + .module_view + .function_acquired_resources(&function_handle); + for acquired_resource in &function_acquired_resources { + if state.is_global_borrowed(*acquired_resource) { + errors.push(err_at_offset(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); + return; + } + } + + let mut all_references_to_borrow_from = BTreeSet::new(); + let mut mutable_references_to_borrow_from = BTreeSet::new(); + let parameters = self.module().signature_at(function_handle.parameters); + for parameter in parameters.0.iter().rev() { + let arg = self.stack.pop().unwrap(); + if arg.signature != instantiate(parameter, type_actuals) { + errors.push(err_at_offset(StatusCode::CALL_TYPE_MISMATCH_ERROR, offset)); + return; + } + if let AbstractValue::Reference(id) = arg.value { + if parameter.is_mutable_reference() { + if state.is_borrowed(id) { + errors.push(err_at_offset( + StatusCode::CALL_BORROWED_MUTABLE_REFERENCE_ERROR, + offset, + )); + return; + } + mutable_references_to_borrow_from.insert(id); + } + all_references_to_borrow_from.insert(id); + } + } + let function_handle_view = FunctionHandleView::new(self.module(), function_handle); + for return_type_view in function_handle_view.return_tokens() { + if return_type_view.is_reference() { + let id = if return_type_view.is_mutable_reference() { + state.borrow_from(&mutable_references_to_borrow_from, true) + } else { + state.borrow_from(&all_references_to_borrow_from, false) + }; + self.stack.push(TypedAbstractValue { + signature: instantiate(return_type_view.as_inner(), type_actuals), + value: AbstractValue::Reference(id), + }); + } else { + let return_type = instantiate(return_type_view.as_inner(), type_actuals); + let k = kind(self.module(), &return_type, self.type_parameters()); + self.stack.push(TypedAbstractValue { + signature: return_type, + value: AbstractValue::Value(k), + }); + } + } + for id in all_references_to_borrow_from { + state.release(id); + } + } + + fn type_fields_signature( + &mut self, + errors: &mut Vec, + offset: usize, + struct_def: &StructDefinition, + type_args: &Signature, + ) -> Option { + let mut field_sig = vec![]; + match &struct_def.field_information { + StructFieldInformation::Native => { + // TODO: this is more of "unreachable" + errors.push(err_at_offset(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)); + return None; + } + StructFieldInformation::Declared(fields) => { + for field_def in fields.iter() { + field_sig.push(instantiate(&field_def.signature.0, type_args)); + } + } + }; + Some(Signature(field_sig)) + } + + fn pack( + &mut self, + errors: &mut Vec, + offset: usize, + struct_def: &StructDefinition, + type_args: &Signature, + ) { + let struct_type = materialize_type(struct_def.struct_handle, type_args); + if let Some(field_sig) = self.type_fields_signature(errors, offset, struct_def, type_args) { + for sig in field_sig.0.iter().rev() { + let arg = self.stack.pop().unwrap(); + if &arg.signature != sig { + errors.push(err_at_offset(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)); + } + } + } + let kind = kind(self.module(), &struct_type, self.type_parameters()); + self.stack.push(TypedAbstractValue { + signature: struct_type, + value: AbstractValue::Value(kind), + }) + } + + fn unpack( + &mut self, + errors: &mut Vec, + offset: usize, + struct_def: &StructDefinition, + type_args: &Signature, + ) { + let struct_type = materialize_type(struct_def.struct_handle, type_args); + + // Pop an abstract value from the stack and check if its type is equal to the one + // declared. TODO: is it safe to not call verify the kinds if the types are equal? + let arg = self.stack.pop().unwrap(); + if arg.signature != struct_type { + errors.push(err_at_offset( + StatusCode::UNPACK_TYPE_MISMATCH_ERROR, + offset, + )); + return; + } + + if let Some(field_sig) = self.type_fields_signature(errors, offset, struct_def, type_args) { + for sig in field_sig.0.into_iter() { + let kind = kind(self.module(), &sig, self.type_parameters()); + self.stack.push(TypedAbstractValue { + signature: sig, + value: AbstractValue::Value(kind), + }) + } + } + } + + fn exists(&mut self, errors: &mut Vec, offset: usize, struct_def: &StructDefinition) { + if !StructDefinitionView::new(self.module(), struct_def).is_nominal_resource() { + errors.push(err_at_offset( + StatusCode::EXISTS_RESOURCE_TYPE_MISMATCH_ERROR, + offset, + )); + return; + } + + let operand = self.stack.pop().unwrap(); + if operand.signature == SignatureToken::Address { + self.stack.push(TypedAbstractValue { + signature: SignatureToken::Bool, + value: AbstractValue::Value(Kind::Copyable), + }); + } else { + errors.push(err_at_offset( + StatusCode::EXISTS_RESOURCE_TYPE_MISMATCH_ERROR, + offset, + )) + } + } + + pub fn move_from( + &mut self, + errors: &mut Vec, + state: &mut AbstractState, + offset: usize, + def_idx: StructDefinitionIndex, + type_args: &Signature, + ) { + let struct_def = self.module().struct_def_at(def_idx); + if !StructDefinitionView::new(self.module(), struct_def).is_nominal_resource() { + errors.push(err_at_offset( + StatusCode::MOVEFROM_NO_RESOURCE_ERROR, + offset, + )); + return; + } else if state.is_global_borrowed(def_idx) { + errors.push(err_at_offset(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); + return; + } + + let struct_type = materialize_type(struct_def.struct_handle, type_args); + let operand = self.stack.pop().unwrap(); + if operand.signature == SignatureToken::Address { + self.stack.push(TypedAbstractValue { + signature: struct_type, + value: AbstractValue::Value(Kind::Resource), + }); + } else { + errors.push(err_at_offset( + StatusCode::MOVEFROM_TYPE_MISMATCH_ERROR, + offset, + )) + } + } + + pub fn move_to( + &mut self, + errors: &mut Vec, + offset: usize, + struct_def: &StructDefinition, + type_args: &Signature, + ) { + if !StructDefinitionView::new(self.module(), struct_def).is_nominal_resource() { + errors.push(err_at_offset( + StatusCode::MOVETOSENDER_NO_RESOURCE_ERROR, + offset, + )); + return; + } + + let struct_type = materialize_type(struct_def.struct_handle, type_args); + let value_operand = self.stack.pop().unwrap(); + if value_operand.signature != struct_type { + errors.push(err_at_offset( + StatusCode::MOVETOSENDER_TYPE_MISMATCH_ERROR, + offset, + )) + } + } + fn execute_inner( &mut self, errors: &mut Vec, @@ -284,9 +511,8 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { match bytecode { Bytecode::Pop => { let operand = self.stack.pop().unwrap(); - let kind = SignatureTokenView::new(self.module(), &operand.signature) - .kind(self.type_formals()); - if kind != Kind::Unrestricted { + let kind = kind(self.module(), &operand.signature, self.type_parameters()); + if kind != Kind::Copyable { errors.push(err_at_offset(StatusCode::POP_RESOURCE_ERROR, offset)); return; } @@ -348,12 +574,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { )); return; } - for return_type_view in self - .function_definition_view - .signature() - .return_tokens() - .rev() - { + for return_type_view in self.function_definition_view.return_tokens().rev() { let operand = self.stack.pop().unwrap(); if operand.signature != *return_type_view.as_inner() { errors.push(err_at_offset(StatusCode::RET_TYPE_MISMATCH_ERROR, offset)); @@ -399,53 +620,75 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { } } - Bytecode::MutBorrowField(field_definition_index) => { - self.borrow_field(errors, state, offset, true, *field_definition_index) + Bytecode::MutBorrowField(field_handle_index) => self.borrow_field( + errors, + state, + offset, + true, + *field_handle_index, + &Signature(vec![]), + ), + + Bytecode::MutBorrowFieldGeneric(field_inst_index) => { + let field_inst = self.module().field_instantiation_at(*field_inst_index); + let type_inst = self.module().signature_at(field_inst.type_parameters); + self.borrow_field(errors, state, offset, true, field_inst.handle, type_inst) } - Bytecode::ImmBorrowField(field_definition_index) => { - self.borrow_field(errors, state, offset, false, *field_definition_index) + Bytecode::ImmBorrowField(field_handle_index) => self.borrow_field( + errors, + state, + offset, + false, + *field_handle_index, + &Signature(vec![]), + ), + + Bytecode::ImmBorrowFieldGeneric(field_inst_index) => { + let field_inst = self.module().field_instantiation_at(*field_inst_index); + let type_inst = self.module().signature_at(field_inst.type_parameters); + self.borrow_field(errors, state, offset, false, field_inst.handle, type_inst) } Bytecode::LdU8(_) => { self.stack.push(TypedAbstractValue { signature: SignatureToken::U8, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } Bytecode::LdU64(_) => { self.stack.push(TypedAbstractValue { signature: SignatureToken::U64, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } Bytecode::LdU128(_) => { self.stack.push(TypedAbstractValue { signature: SignatureToken::U128, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } Bytecode::LdAddr(_) => { self.stack.push(TypedAbstractValue { signature: SignatureToken::Address, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } Bytecode::LdByteArray(_) => { self.stack.push(TypedAbstractValue { signature: SignatureToken::Vector(Box::new(SignatureToken::U8)), - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } Bytecode::LdTrue | Bytecode::LdFalse => { self.stack.push(TypedAbstractValue { signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } @@ -461,15 +704,19 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { value: AbstractValue::Reference(id), }); } else { - match signature_view.kind(self.type_formals()) { + match kind( + self.module(), + signature_view.signature_token(), + self.type_parameters(), + ) { Kind::Resource | Kind::All => { errors.push(err_at_offset(StatusCode::COPYLOC_RESOURCE_ERROR, offset)) } - Kind::Unrestricted => { + Kind::Copyable => { if !state.is_local_mutably_borrowed(*idx) { self.stack.push(TypedAbstractValue { signature: signature_view.as_inner().clone(), - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }) } else { errors.push(err_at_offset( @@ -501,163 +748,40 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { Bytecode::ImmBorrowLoc(idx) => self.borrow_loc(errors, state, offset, false, *idx), - Bytecode::Call(idx, type_actuals_idx) => { + Bytecode::Call(idx) => { let function_handle = self.module().function_handle_at(*idx); - let function_signature = self - .module() - .function_signature_at(function_handle.signature); - - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - - let function_acquired_resources = self - .module_view - .function_acquired_resources(&function_handle); - for acquired_resource in &function_acquired_resources { - if state.is_global_borrowed(*acquired_resource) { - errors.push(err_at_offset(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); - return; - } - } + self.call(errors, state, offset, function_handle, &Signature(vec![])) + } - let function_signature_view = - FunctionSignatureView::new(self.module(), function_signature); - let mut all_references_to_borrow_from = BTreeSet::new(); - let mut mutable_references_to_borrow_from = BTreeSet::new(); - for arg_type in function_signature.arg_types.iter().rev() { - let arg = self.stack.pop().unwrap(); - if arg.signature != arg_type.substitute(type_actuals) { - errors.push(err_at_offset(StatusCode::CALL_TYPE_MISMATCH_ERROR, offset)); - return; - } - if let AbstractValue::Reference(id) = arg.value { - if arg_type.is_mutable_reference() { - if state.is_borrowed(id) { - errors.push(err_at_offset( - StatusCode::CALL_BORROWED_MUTABLE_REFERENCE_ERROR, - offset, - )); - return; - } - mutable_references_to_borrow_from.insert(id); - } - all_references_to_borrow_from.insert(id); - } - } - for return_type_view in function_signature_view.return_tokens() { - if return_type_view.is_reference() { - let id = if return_type_view.is_mutable_reference() { - state.borrow_from(&mutable_references_to_borrow_from, true) - } else { - state.borrow_from(&all_references_to_borrow_from, false) - }; - self.stack.push(TypedAbstractValue { - signature: return_type_view.as_inner().substitute(type_actuals), - value: AbstractValue::Reference(id), - }); - } else { - let return_type = return_type_view.as_inner().substitute(type_actuals); - let kind = SignatureTokenView::new(self.module(), &return_type) - .kind(self.type_formals()); - self.stack.push(TypedAbstractValue { - signature: return_type, - value: AbstractValue::Value(kind), - }); - } - } - for id in all_references_to_borrow_from { - state.release(id); - } + Bytecode::CallGeneric(idx) => { + let func_inst = self.module().function_instantiation_at(*idx); + let func_handle = self.module().function_handle_at(func_inst.handle); + let type_args = &self.module().signature_at(func_inst.type_parameters); + self.call(errors, state, offset, func_handle, type_args) } - Bytecode::Pack(idx, type_actuals_idx) => { - // Build and verify the struct type. + Bytecode::Pack(idx) => { let struct_definition = self.module().struct_def_at(*idx); - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - let kind = - SignatureTokenView::new(self.module(), &struct_type).kind(self.type_formals()); - - let struct_definition_view = - StructDefinitionView::new(self.module(), struct_definition); - match struct_definition_view.fields() { - None => { - // TODO pack on native error - errors.push(err_at_offset(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)); - } - Some(fields) => { - for field_definition_view in fields.rev() { - let field_signature_view = field_definition_view.type_signature(); - // Substitute type variables with actual types. - let field_type = field_signature_view - .token() - .as_inner() - .substitute(type_actuals); - // TODO: is it necessary to verify kind constraints here? - let arg = self.stack.pop().unwrap(); - if arg.signature != field_type { - errors.push(err_at_offset( - StatusCode::PACK_TYPE_MISMATCH_ERROR, - offset, - )); - } - } - } - } + self.pack(errors, offset, struct_definition, &Signature(vec![])) + } - self.stack.push(TypedAbstractValue { - signature: struct_type, - value: AbstractValue::Value(kind), - }) + Bytecode::PackGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let struct_def = self.module().struct_def_at(struct_inst.def); + let type_args = &self.module().signature_at(struct_inst.type_parameters); + self.pack(errors, offset, struct_def, type_args) } - Bytecode::Unpack(idx, type_actuals_idx) => { - // Build and verify the struct type. + Bytecode::Unpack(idx) => { let struct_definition = self.module().struct_def_at(*idx); - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - - // Pop an abstract value from the stack and check if its type is equal to the one - // declared. TODO: is it safe to not call verify the kinds if the types are equal? - let arg = self.stack.pop().unwrap(); - if arg.signature != struct_type { - errors.push(err_at_offset( - StatusCode::UNPACK_TYPE_MISMATCH_ERROR, - offset, - )); - return; - } + self.unpack(errors, offset, struct_definition, &Signature(vec![])) + } - // For each field, push an abstract value to the stack. - let struct_definition_view = - StructDefinitionView::new(self.module(), struct_definition); - match struct_definition_view.fields() { - None => { - // TODO unpack on native error - errors.push(err_at_offset( - StatusCode::UNPACK_TYPE_MISMATCH_ERROR, - offset, - )); - } - Some(fields) => { - for field_definition_view in fields { - let field_signature_view = field_definition_view.type_signature(); - // Substitute type variables with actual types. - let field_type = field_signature_view - .token() - .as_inner() - .substitute(type_actuals); - // Get the kind of the type. - let kind = SignatureTokenView::new(self.module(), &field_type) - .kind(self.type_formals()); - self.stack.push(TypedAbstractValue { - signature: field_type, - value: AbstractValue::Value(kind), - }) - } - } - } + Bytecode::UnpackGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let struct_def = self.module().struct_def_at(struct_inst.def); + let type_args = &self.module().signature_at(struct_inst.type_parameters); + self.unpack(errors, offset, struct_def, type_args) } Bytecode::ReadRef => { @@ -684,15 +808,14 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { SignatureToken::MutableReference(signature) => signature, _ => panic!("Unreachable"), }; - if SignatureTokenView::new(self.module(), &inner_signature) - .kind(self.type_formals()) - != Kind::Unrestricted + if kind(self.module(), &inner_signature, self.type_parameters()) + != Kind::Copyable { errors.push(err_at_offset(StatusCode::READREF_RESOURCE_ERROR, offset)) } else { self.stack.push(TypedAbstractValue { signature: inner_signature, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); state.release(operand_id); } @@ -703,13 +826,12 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { let ref_operand = self.stack.pop().unwrap(); let val_operand = self.stack.pop().unwrap(); if let SignatureToken::MutableReference(signature) = ref_operand.signature { - let kind = SignatureTokenView::new(self.module(), &signature) - .kind(self.type_formals()); + let kind = kind(self.module(), &signature, self.type_parameters()); match kind { Kind::Resource | Kind::All => { errors.push(err_at_offset(StatusCode::WRITEREF_RESOURCE_ERROR, offset)) } - Kind::Unrestricted => { + Kind::Copyable => { if val_operand.signature != *signature { errors.push(err_at_offset( StatusCode::WRITEREF_TYPE_MISMATCH_ERROR, @@ -741,7 +863,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand.signature.is_integer() { self.stack.push(TypedAbstractValue { signature: SignatureToken::U8, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -755,7 +877,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand.signature.is_integer() { self.stack.push(TypedAbstractValue { signature: SignatureToken::U64, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -769,7 +891,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand.signature.is_integer() { self.stack.push(TypedAbstractValue { signature: SignatureToken::U128, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -792,7 +914,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand1.signature.is_integer() && operand1.signature == operand2.signature { self.stack.push(TypedAbstractValue { signature: operand1.signature, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -808,7 +930,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand2.signature.is_integer() && operand1.signature == SignatureToken::U8 { self.stack.push(TypedAbstractValue { signature: operand2.signature, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -826,7 +948,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { { self.stack.push(TypedAbstractValue { signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -841,7 +963,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand.signature == SignatureToken::Bool { self.stack.push(TypedAbstractValue { signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -854,9 +976,8 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { Bytecode::Eq | Bytecode::Neq => { let operand1 = self.stack.pop().unwrap(); let operand2 = self.stack.pop().unwrap(); - let kind1 = SignatureTokenView::new(self.module(), &operand1.signature) - .kind(self.type_formals()); - let is_copyable = kind1 == Kind::Unrestricted; + let kind1 = kind(self.module(), &operand1.signature, self.type_parameters()); + let is_copyable = kind1 == Kind::Copyable; if is_copyable && operand1.signature == operand2.signature { if let (AbstractValue::Reference(id1), AbstractValue::Reference(id2)) = (operand1.value, operand2.value) @@ -876,7 +997,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { } self.stack.push(TypedAbstractValue { signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -892,7 +1013,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { if operand1.signature.is_integer() && operand1.signature == operand2.signature { self.stack.push(TypedAbstractValue { signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } else { errors.push(err_at_offset( @@ -902,103 +1023,57 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { } } - Bytecode::Exists(idx, type_actuals_idx) => { - let struct_definition = self.module().struct_def_at(*idx); - if !StructDefinitionView::new(self.module(), struct_definition) - .is_nominal_resource() - { - errors.push(err_at_offset( - StatusCode::EXISTS_RESOURCE_TYPE_MISMATCH_ERROR, - offset, - )); - return; - } - - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - SignatureTokenView::new(self.module(), &struct_type).kind(self.type_formals()); + Bytecode::MutBorrowGlobal(idx) => { + self.borrow_global(errors, state, offset, true, *idx, &Signature(vec![])) + } - let operand = self.stack.pop().unwrap(); - if operand.signature == SignatureToken::Address { - self.stack.push(TypedAbstractValue { - signature: SignatureToken::Bool, - value: AbstractValue::Value(Kind::Unrestricted), - }); - } else { - errors.push(err_at_offset( - StatusCode::EXISTS_RESOURCE_TYPE_MISMATCH_ERROR, - offset, - )) - } + Bytecode::MutBorrowGlobalGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let type_inst = self.module().signature_at(struct_inst.type_parameters); + self.borrow_global(errors, state, offset, true, struct_inst.def, type_inst) } - Bytecode::MutBorrowGlobal(idx, type_actuals_idx) => { - self.borrow_global(errors, state, offset, true, *idx, *type_actuals_idx) + Bytecode::ImmBorrowGlobal(idx) => { + self.borrow_global(errors, state, offset, false, *idx, &Signature(vec![])) } - Bytecode::ImmBorrowGlobal(idx, type_actuals_idx) => { - self.borrow_global(errors, state, offset, false, *idx, *type_actuals_idx) + Bytecode::ImmBorrowGlobalGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let type_inst = self.module().signature_at(struct_inst.type_parameters); + self.borrow_global(errors, state, offset, false, struct_inst.def, type_inst) } - Bytecode::MoveFrom(idx, type_actuals_idx) => { - let struct_definition = self.module().struct_def_at(*idx); - if !StructDefinitionView::new(self.module(), struct_definition) - .is_nominal_resource() - { - errors.push(err_at_offset( - StatusCode::MOVEFROM_NO_RESOURCE_ERROR, - offset, - )); - return; - } else if state.is_global_borrowed(*idx) { - errors.push(err_at_offset(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); - return; - } + Bytecode::Exists(idx) => { + let struct_def = self.module().struct_def_at(*idx); + self.exists(errors, offset, struct_def) + } - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - SignatureTokenView::new(self.module(), &struct_type).kind(self.type_formals()); + Bytecode::ExistsGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let struct_def = self.module().struct_def_at(struct_inst.def); + self.exists(errors, offset, struct_def) + } - let operand = self.stack.pop().unwrap(); - if operand.signature == SignatureToken::Address { - self.stack.push(TypedAbstractValue { - signature: struct_type, - value: AbstractValue::Value(Kind::Resource), - }); - } else { - errors.push(err_at_offset( - StatusCode::MOVEFROM_TYPE_MISMATCH_ERROR, - offset, - )) - } + Bytecode::MoveFrom(idx) => { + self.move_from(errors, state, offset, *idx, &Signature(vec![])) } - Bytecode::MoveToSender(idx, type_actuals_idx) => { - let struct_definition = self.module().struct_def_at(*idx); - if !StructDefinitionView::new(self.module(), struct_definition) - .is_nominal_resource() - { - errors.push(err_at_offset( - StatusCode::MOVETOSENDER_NO_RESOURCE_ERROR, - offset, - )); - return; - } + Bytecode::MoveFromGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let type_args = &self.module().signature_at(struct_inst.type_parameters); + self.move_from(errors, state, offset, struct_inst.def, type_args) + } - let type_actuals = &self.module().locals_signature_at(*type_actuals_idx).0; - let struct_type = - SignatureToken::Struct(struct_definition.struct_handle, type_actuals.clone()); - SignatureTokenView::new(self.module(), &struct_type).kind(self.type_formals()); + Bytecode::MoveToSender(idx) => { + let struct_def = self.module().struct_def_at(*idx); + self.move_to(errors, offset, struct_def, &Signature(vec![])) + } - let value_operand = self.stack.pop().unwrap(); - if value_operand.signature != struct_type { - errors.push(err_at_offset( - StatusCode::MOVETOSENDER_TYPE_MISMATCH_ERROR, - offset, - )) - } + Bytecode::MoveToSenderGeneric(idx) => { + let struct_inst = self.module().struct_instantiation_at(*idx); + let struct_def = self.module().struct_def_at(struct_inst.def); + let type_args = &self.module().signature_at(struct_inst.type_parameters); + self.move_to(errors, offset, struct_def, type_args) } Bytecode::GetTxnGasUnitPrice @@ -1017,7 +1092,7 @@ impl<'a> TypeAndMemorySafetyAnalysis<'a> { Bytecode::GetTxnSenderAddress => { self.stack.push(TypedAbstractValue { signature: SignatureToken::Address, - value: AbstractValue::Value(Kind::Unrestricted), + value: AbstractValue::Value(Kind::Copyable), }); } } @@ -1049,3 +1124,40 @@ impl<'a> TransferFunctions for TypeAndMemorySafetyAnalysis<'a> { } impl<'a> AbstractInterpreter for TypeAndMemorySafetyAnalysis<'a> {} + +fn materialize_type(struct_handle: StructHandleIndex, type_args: &Signature) -> SignatureToken { + if type_args.is_empty() { + SignatureToken::Struct(struct_handle) + } else { + SignatureToken::StructInstantiation(struct_handle, type_args.0.clone()) + } +} + +fn instantiate(token: &SignatureToken, subst: &Signature) -> SignatureToken { + use SignatureToken::*; + + match token { + Bool => Bool, + U8 => U8, + U64 => U64, + U128 => U128, + Address => Address, + Vector(ty) => Vector(Box::new(instantiate(ty, subst))), + Struct(idx) => Struct(*idx), + StructInstantiation(idx, struct_type_args) => StructInstantiation( + *idx, + struct_type_args + .iter() + .map(|ty| instantiate(ty, subst)) + .collect(), + ), + Reference(ty) => Reference(Box::new(instantiate(ty, subst))), + MutableReference(ty) => MutableReference(Box::new(instantiate(ty, subst))), + 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. + assume!((*idx as usize) < subst.len()); + subst.0[*idx as usize].clone() + } + } +} diff --git a/language/bytecode-verifier/src/unused_entries.rs b/language/bytecode-verifier/src/unused_entries.rs index b33bd30fedde..9d4707936797 100644 --- a/language/bytecode-verifier/src/unused_entries.rs +++ b/language/bytecode-verifier/src/unused_entries.rs @@ -5,25 +5,21 @@ use libra_types::vm_error::{StatusCode, VMStatus}; use vm::{ access::ModuleAccess, errors::verification_error, - file_format::{Bytecode, CompiledModule, StructFieldInformation}, + file_format::{Bytecode, CompiledModule}, IndexKind, }; pub struct UnusedEntryChecker<'a> { module: &'a CompiledModule, - field_defs: Vec, - locals_signatures: Vec, - type_signatures: Vec, + signatures: Vec, } impl<'a> UnusedEntryChecker<'a> { pub fn new(module: &'a CompiledModule) -> Self { Self { module, - field_defs: vec![false; module.field_defs().len()], - locals_signatures: vec![false; module.locals_signatures().len()], - type_signatures: vec![false; module.type_signatures().len()], + signatures: vec![false; module.signatures().len()], } } @@ -34,43 +30,29 @@ impl<'a> UnusedEntryChecker<'a> { if func_def.is_native() { continue; } - self.locals_signatures[func_def.code.locals.0 as usize] = true; + self.signatures[func_def.code.locals.0 as usize] = true; for bytecode in &func_def.code.code { match bytecode { - Call(_, idx) - | Pack(_, idx) - | Unpack(_, idx) - | MutBorrowGlobal(_, idx) - | ImmBorrowGlobal(_, idx) - | Exists(_, idx) - | MoveToSender(_, idx) - | MoveFrom(_, idx) => { - self.locals_signatures[idx.0 as usize] = true; + CallGeneric(idx) => { + let func_inst = self.module.function_instantiation_at(*idx); + self.signatures[func_inst.type_parameters.0 as usize] = true; } - _ => (), - } - } - } - } - - fn traverse_struct_defs(&mut self) { - for struct_def in self.module.struct_defs() { - match struct_def.field_information { - StructFieldInformation::Native => (), - StructFieldInformation::Declared { - field_count, - fields, - } => { - let start = fields.0 as usize; - let end = start + (field_count as usize); - - for i in start..end { - self.field_defs[i] = true; - - let field_def = &self.module.field_defs()[i]; - self.type_signatures[field_def.signature.0 as usize] = true; + MutBorrowFieldGeneric(idx) | ImmBorrowFieldGeneric(idx) => { + let field_inst = self.module.field_instantiation_at(*idx); + self.signatures[field_inst.type_parameters.0 as usize] = true; } + PackGeneric(idx) + | UnpackGeneric(idx) + | MutBorrowGlobalGeneric(idx) + | ImmBorrowGlobalGeneric(idx) + | ExistsGeneric(idx) + | MoveToSenderGeneric(idx) + | MoveFromGeneric(idx) => { + let struct_inst = self.module.struct_instantiation_at(*idx); + self.signatures[struct_inst.type_parameters.0 as usize] = true; + } + _ => (), } } } @@ -86,32 +68,15 @@ impl<'a> UnusedEntryChecker<'a> { } pub fn verify(mut self) -> Vec { - self.traverse_struct_defs(); self.traverse_function_defs(); - let iter_field_defs = Self::collect_errors(&self.field_defs, |idx| { - verification_error(IndexKind::FieldDefinition, idx, StatusCode::UNUSED_FIELD) - }); - - let iter_locals_signatures = Self::collect_errors(&self.locals_signatures, |idx| { + Self::collect_errors(&self.signatures, |idx| { verification_error( - IndexKind::LocalsSignature, + IndexKind::Signature, idx, StatusCode::UNUSED_LOCALS_SIGNATURE, ) - }); - - let iter_type_signatures = Self::collect_errors(&self.type_signatures, |idx| { - verification_error( - IndexKind::TypeSignature, - idx, - StatusCode::UNUSED_TYPE_SIGNATURE, - ) - }); - - iter_field_defs - .chain(iter_locals_signatures) - .chain(iter_type_signatures) - .collect() + }) + .collect() } } diff --git a/language/bytecode-verifier/src/verifier.rs b/language/bytecode-verifier/src/verifier.rs index ed37904e6f5f..519b5b265b49 100644 --- a/language/bytecode-verifier/src/verifier.rs +++ b/language/bytecode-verifier/src/verifier.rs @@ -4,8 +4,9 @@ //! This module contains the public APIs supported by the bytecode verifier. use crate::{ check_duplication::DuplicationChecker, code_unit_verifier::CodeUnitVerifier, - instantiation_loops::InstantiationLoopChecker, resources::ResourceTransitiveChecker, - signature::SignatureChecker, struct_defs::RecursiveStructDefChecker, + instantiation_loops::InstantiationLoopChecker, resolver::Resolver, + resources::ResourceTransitiveChecker, signature::SignatureChecker, + struct_defs::RecursiveStructDefChecker, }; use anyhow::Error; use libra_types::{ @@ -13,12 +14,11 @@ use libra_types::{ vm_error::{StatusCode, VMStatus}, }; use move_vm_types::native_functions::dispatch::NativeFunction; -use std::{collections::BTreeMap, fmt}; +use std::collections::BTreeMap; use vm::{ access::{ModuleAccess, ScriptAccess}, errors::{append_err_info, verification_error}, file_format::{CompiledModule, CompiledScript, SignatureToken}, - resolver::Resolver, views::{ModuleView, ViewInternals}, IndexKind, }; @@ -48,7 +48,7 @@ impl VerifiedModule { errors.append(&mut RecursiveStructDefChecker::new(&module).verify()); } if errors.is_empty() { - errors.append(&mut InstantiationLoopChecker::new(&module).verify()) + errors.append(&mut InstantiationLoopChecker::new(&module).verify()); } if errors.is_empty() { errors.append(&mut CodeUnitVerifier::verify(&module)); @@ -98,12 +98,6 @@ impl ModuleAccess for VerifiedModule { } } -impl fmt::Display for VerifiedModule { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VerifiedModule: {}", self.0) - } -} - /// A script that has been verified for internal consistency. /// /// This does not include cross-module checking -- that needs to be done separately. @@ -180,21 +174,16 @@ impl ScriptAccess for VerifiedScript { } } -impl fmt::Display for VerifiedScript { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VerifiedScript: {}", self.0) - } -} - /// This function checks the extra requirements on the signature of the main function of a script. pub fn verify_main_signature(script: &CompiledScript) -> Vec { let function_handle = &script.function_handle_at(script.main().function); - let function_signature = &script.function_signature_at(function_handle.signature); - if !function_signature.return_types.is_empty() { + let return_ = script.signature_at(function_handle.return_); + if !return_.is_empty() { return vec![VMStatus::new(StatusCode::INVALID_MAIN_FUNCTION_SIGNATURE)]; } - for arg_type in &function_signature.arg_types { + let arguments = script.signature_at(function_handle.parameters); + for arg_type in &arguments.0 { if !(arg_type.is_primitive() || *arg_type == SignatureToken::Vector(Box::new(SignatureToken::U8))) { @@ -269,26 +258,89 @@ fn verify_native_functions(module_view: &ModuleView) -> Vec { - let declared_function_signature = - native_function_definition_view.signature().as_inner(); - let expected_function_signature_res = - vm_native_function.signature(Some(module_view)); - let expected_function_signature_opt = match expected_function_signature_res { - Ok(opt) => opt, + // check parameters + let def_params = native_function_definition_view.parameters(); + let native_params = match vm_native_function.parameters(Some(module_view)) { + Ok(opt) => match opt { + None => { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; + } + Some(sig) => sig, + }, + Err(e) => { + errors.push(e); + continue; + } + }; + if def_params != &native_params { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; + } + + // check return_ + let def_return_ = native_function_definition_view.return_(); + let native_return_ = match vm_native_function.return_(Some(module_view)) { + Ok(opt) => match opt { + None => { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; + } + Some(sig) => sig, + }, Err(e) => { errors.push(e); continue; } }; - let matching_signatures = expected_function_signature_opt - .map(|e| &e == declared_function_signature) - .unwrap_or(false); - if !matching_signatures { + if def_return_ != &native_return_ { errors.push(verification_error( IndexKind::FunctionHandle, idx, StatusCode::TYPE_MISMATCH, - )) + )); + continue; + } + + // check type parameters + let def_type_parameters = native_function_definition_view.type_parameters(); + let native_type_parameters = + match vm_native_function.type_parameters(Some(module_view)) { + Ok(opt) => match opt { + None => { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; + } + Some(t_params) => t_params, + }, + Err(e) => { + errors.push(e); + continue; + } + }; + if def_type_parameters != &native_type_parameters { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; } } } @@ -344,7 +396,7 @@ fn verify_struct_kind( if let Some(struct_definition_view) = owner_module_view.struct_definition(struct_name) { if struct_handle_view.is_nominal_resource() != struct_definition_view.is_nominal_resource() - || struct_handle_view.type_formals() != struct_definition_view.type_formals() + || struct_handle_view.type_parameters() != struct_definition_view.type_parameters() { errors.push(verification_error( IndexKind::StructHandle, @@ -372,6 +424,7 @@ fn verify_function_visibility_and_type( for (idx, function_handle_view) in module_view.function_handles().enumerate() { let owner_module_id = function_handle_view.module_id(); if !dependency_map.contains_key(&owner_module_id) { + // REVIEW: when does it happen? definitions? should we check correctness? continue; } let function_name = function_handle_view.name(); @@ -379,31 +432,41 @@ fn verify_function_visibility_and_type( let owner_module_view = ModuleView::new(owner_module); if let Some(function_definition_view) = owner_module_view.function_definition(function_name) { - if function_definition_view.is_public() { - let function_definition_signature = function_definition_view.signature().as_inner(); - match resolver - .import_function_signature(owner_module, &function_definition_signature) - { - Ok(imported_function_signature) => { - let function_handle_signature = function_handle_view.signature().as_inner(); - if imported_function_signature != *function_handle_signature { - errors.push(verification_error( - IndexKind::FunctionHandle, - idx, - StatusCode::TYPE_MISMATCH, - )); - } - } - Err(err) => { - errors.push(append_err_info(err, IndexKind::FunctionHandle, idx)); - } - } - } else { + if !function_definition_view.is_public() { errors.push(verification_error( IndexKind::FunctionHandle, idx, StatusCode::VISIBILITY_MISMATCH, )); + continue; + } + // same type parameter constraints + if function_definition_view.type_parameters() != function_handle_view.type_parameters() + { + errors.push(verification_error( + IndexKind::FunctionHandle, + idx, + StatusCode::TYPE_MISMATCH, + )); + continue; + } + // same parameters + let handle_params = function_handle_view.parameters(); + let def_params = function_definition_view.parameters(); + if let Err(err) = + resolver.compare_cross_module_signatures(handle_params, def_params, owner_module) + { + errors.push(append_err_info(err, IndexKind::FunctionHandle, idx)); + continue; + } + // same return_ + let handle_return = function_handle_view.return_(); + let def_return = function_definition_view.return_(); + if let Err(err) = + resolver.compare_cross_module_signatures(handle_return, def_return, owner_module) + { + errors.push(append_err_info(err, IndexKind::FunctionHandle, idx)); + continue; } } else { errors.push(verification_error( @@ -411,8 +474,10 @@ fn verify_function_visibility_and_type( idx, StatusCode::LOOKUP_FAILED, )); + continue; } } + errors } diff --git a/language/compiler/bytecode-source-map/src/marking.rs b/language/compiler/bytecode-source-map/src/marking.rs index de9cab780f27..96202ed6d5c3 100644 --- a/language/compiler/bytecode-source-map/src/marking.rs +++ b/language/compiler/bytecode-source-map/src/marking.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use vm::file_format::{ - CodeOffset, FieldDefinitionIndex, FunctionDefinitionIndex, StructDefinitionIndex, TableIndex, + CodeOffset, FunctionDefinitionIndex, MemberCount, StructDefinitionIndex, TableIndex, }; /// A data structure used to track any markings or extra information that is desired to be exposed @@ -22,7 +22,7 @@ pub struct FunctionMarking { #[derive(Debug, Default)] pub struct StructMarking { // Field markings - pub fields: BTreeMap>, + pub fields: BTreeMap>, // Type parameter markings pub type_param_offsets: BTreeMap>, @@ -70,7 +70,7 @@ impl StructMarking { } } - pub fn field(&mut self, field_index: FieldDefinitionIndex, message: String) { + pub fn field(&mut self, field_index: MemberCount, message: String) { self.fields .entry(field_index) .or_insert_with(Vec::new) @@ -120,13 +120,13 @@ impl MarkedSourceMapping { pub fn mark_struct_field( &mut self, struct_definition_index: StructDefinitionIndex, - field_def_index: FieldDefinitionIndex, + field_index: MemberCount, message: String, ) { self.struct_marks .entry(struct_definition_index.0) .or_insert_with(StructMarking::new) - .field(field_def_index, message) + .field(field_index, message) } pub fn mark_struct_type_param( diff --git a/language/compiler/bytecode-source-map/src/source_map.rs b/language/compiler/bytecode-source-map/src/source_map.rs index 363c681580f0..98fec8644b54 100644 --- a/language/compiler/bytecode-source-map/src/source_map.rs +++ b/language/compiler/bytecode-source-map/src/source_map.rs @@ -10,11 +10,10 @@ use std::{collections::BTreeMap, ops::Bound}; use vm::{ access::*, file_format::{ - AddressPoolIndex, CodeOffset, CompiledModule, CompiledScript, FieldDefinitionIndex, - FunctionDefinition, FunctionDefinitionIndex, IdentifierIndex, StructDefinition, + AddressPoolIndex, CodeOffset, CompiledModule, CompiledScript, FunctionDefinition, + FunctionDefinitionIndex, IdentifierIndex, MemberCount, StructDefinition, StructDefinitionIndex, TableIndex, }, - internals::ModuleIndex, }; //*************************************************************************** @@ -108,8 +107,8 @@ impl StructSourceMap { self.fields.push(field_loc) } - pub fn get_field_location(&self, field_index: FieldDefinitionIndex) -> Option { - self.fields.get(field_index.into_index()).cloned() + pub fn get_field_location(&self, field_index: MemberCount) -> Option { + self.fields.get(field_index as usize).cloned() } pub fn dummy_struct_map( @@ -126,7 +125,7 @@ impl StructSourceMap { Ok(count) => (0..count).for_each(|_| self.fields.push(default_loc.clone())), } - for i in 0..struct_handle.type_formals.len() { + for i in 0..struct_handle.type_parameters.len() { let name = format!("Ty{}", i); self.add_type_parameter((name, default_loc.clone())) } @@ -226,17 +225,16 @@ impl FunctionSourceMap { default_loc: Location, ) -> Result<()> { let function_handle = module.function_handle_at(function_def.function); - let function_signature = module.function_signature_at(function_handle.signature); let function_code = &function_def.code; // Generate names for each type parameter - for i in 0..function_signature.type_formals.len() { + for i in 0..function_handle.type_parameters.len() { let name = format!("Ty{}", i); self.add_type_parameter((name, default_loc.clone())) } if !function_def.is_native() { - let locals = module.locals_signature_at(function_code.locals); + let locals = module.signature_at(function_code.locals); for i in 0..locals.0.len() { let name = format!("loc{}", i); self.add_local_mapping((name, default_loc.clone())) @@ -417,7 +415,7 @@ impl SourceMap { pub fn get_struct_field_name( &self, struct_def_idx: StructDefinitionIndex, - field_idx: FieldDefinitionIndex, + field_idx: MemberCount, ) -> Option { self.struct_map .get(&struct_def_idx.0) diff --git a/language/compiler/ir-to-bytecode/src/compiler.rs b/language/compiler/ir-to-bytecode/src/compiler.rs index e1c5f710dd43..f4f8ea385eeb 100644 --- a/language/compiler/ir-to-bytecode/src/compiler.rs +++ b/language/compiler/ir-to-bytecode/src/compiler.rs @@ -26,9 +26,9 @@ use vm::{ access::ModuleAccess, file_format::{ self, Bytecode, CodeOffset, CodeUnit, CompiledModule, CompiledModuleMut, CompiledScript, - CompiledScriptMut, FieldDefinition, FieldDefinitionIndex, FunctionDefinition, - FunctionSignature, Kind, LocalsSignature, MemberCount, SignatureToken, StructDefinition, - StructFieldInformation, StructHandleIndex, TableIndex, TypeParameterIndex, + CompiledScriptMut, FieldDefinition, FunctionDefinition, FunctionSignature, Kind, Signature, + SignatureToken, StructDefinition, StructDefinitionIndex, StructFieldInformation, + StructHandleIndex, TableIndex, TypeParameterIndex, TypeSignature, }, }; @@ -39,10 +39,10 @@ macro_rules! record_src_loc { .source_map .add_local_mapping($context.current_function_definition_index(), source_name)?; }}; - (field: $context:expr, $field:expr) => {{ + (field: $context:expr, $idx: expr, $field:expr) => {{ $context .source_map - .add_struct_field_mapping($context.current_struct_definition_index(), $field.loc)?; + .add_struct_field_mapping($idx, $field.loc)?; }}; (function_type_formals: $context:expr, $var:expr) => { for (ty_var, _) in $var.iter() { @@ -179,7 +179,11 @@ impl InferredType { S::Vector(s_inner) => I::Vector(Box::new(Self::from_signature_token_with_subst( subst, s_inner, ))), - S::Struct(si, sig_tys) => { + S::Struct(si) => { + let tys = Self::from_signature_tokens_with_subst(subst, &[]); + I::Struct(*si, tys) + } + S::StructInstantiation(si, sig_tys) => { let tys = Self::from_signature_tokens_with_subst(subst, sig_tys); I::Struct(*si, tys) } @@ -232,13 +236,45 @@ impl InferredType { InferredType::TypeParameter(_) => bail!("no struct type for type parameter"), } } + + fn to_signature_token(ty: &Self) -> Result { + use InferredType as I; + use SignatureToken as S; + Ok(match ty { + I::Bool => S::Bool, + I::U8 => S::U8, + I::U64 => S::U64, + I::U128 => S::U128, + I::Address => S::Address, + I::Vector(inner) => S::Vector(Box::new(Self::to_signature_token(inner)?)), + I::Struct(si, tys) if tys.is_empty() => S::Struct(*si), + I::Struct(si, tys) => S::StructInstantiation(*si, Self::build_signature_tokens(tys)?), + I::Reference(inner) => S::Reference(Box::new(Self::to_signature_token(inner)?)), + I::MutableReference(inner) => { + S::MutableReference(Box::new(Self::to_signature_token(inner)?)) + } + I::TypeParameter(s) => match s.parse::() { + Ok(idx) => S::TypeParameter(idx), + Err(_) => bail!( + "ICE unsubstituted type parameter when converting back to signature tokens" + ), + }, + I::Anything => bail!("Could not infer type"), + }) + } + + fn build_signature_tokens(tys: &[InferredType]) -> Result> { + tys.iter() + .map(|sig_ty| Self::to_signature_token(sig_ty)) + .collect() + } } // Holds information about a function being compiled. #[derive(Debug)] struct FunctionFrame { locals: HashMap, - local_types: LocalsSignature, + local_types: Signature, // i64 to allow the bytecode verifier to catch errors of // - negative stack sizes // - excessivley large stack sizes @@ -254,7 +290,7 @@ impl FunctionFrame { fn new(type_parameters: HashMap) -> FunctionFrame { FunctionFrame { locals: HashMap::new(), - local_types: LocalsSignature(vec![]), + local_types: Signature(vec![]), max_stack_depth: 0, cur_stack_depth: 0, loops: vec![], @@ -386,12 +422,12 @@ pub fn compile_script<'a, T: 'a + ModuleAccess>( module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + signatures, identifiers, byte_array_pool, address_pool, + function_instantiations, + .. }, source_map, ) = context.materialize_pools(); @@ -399,9 +435,8 @@ pub fn compile_script<'a, T: 'a + ModuleAccess>( module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + function_instantiations, + signatures, identifiers, byte_array_pool, address_pool, @@ -451,7 +486,7 @@ pub fn compile_module<'a, T: 'a + ModuleAccess>( // Current module - let (struct_defs, field_defs) = compile_structs(&mut context, &self_name, module.structs)?; + let struct_defs = compile_structs(&mut context, &self_name, module.structs)?; let function_defs = compile_functions(&mut context, &self_name, module.functions)?; @@ -460,12 +495,14 @@ pub fn compile_module<'a, T: 'a + ModuleAccess>( module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + field_handles, + signatures, identifiers, byte_array_pool, address_pool, + function_instantiations, + struct_def_instantiations, + field_instantiations, }, source_map, ) = context.materialize_pools(); @@ -473,14 +510,15 @@ pub fn compile_module<'a, T: 'a + ModuleAccess>( module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + field_handles, + struct_def_instantiations, + function_instantiations, + field_instantiations, + signatures, identifiers, byte_array_pool, address_pool, struct_defs, - field_defs, function_defs, }; compiled_module @@ -570,7 +608,7 @@ fn kind(ast_k: &ast::Kind) -> Kind { match ast_k { ast::Kind::All => Kind::All, ast::Kind::Resource => Kind::Resource, - ast::Kind::Unrestricted => Kind::Unrestricted, + ast::Kind::Copyable => Kind::Copyable, } } @@ -610,8 +648,13 @@ fn compile_type( } Type::Struct(ident, tys) => { let sh_idx = context.struct_handle_index(ident.clone())?; - let tokens = compile_types(context, type_parameters, tys)?; - SignatureToken::Struct(sh_idx, tokens) + + if tys.is_empty() { + SignatureToken::Struct(sh_idx) + } else { + let tokens = compile_types(context, type_parameters, tys)?; + SignatureToken::StructInstantiation(sh_idx, tokens) + } } Type::TypeParameter(ty_var) => { let idx = match type_parameters.get(&ty_var) { @@ -628,17 +671,17 @@ fn function_signature( f: &ast::FunctionSignature, ) -> Result { let m = type_parameter_indexes(&f.type_formals)?; - let return_types = compile_types(context, &m, &f.return_type)?; - let arg_types = f + let return_ = compile_types(context, &m, &f.return_type)?; + let parameters = f .formals .iter() .map(|(_, ty)| compile_type(context, &m, ty)) .collect::>()?; - let type_formals = f.type_formals.iter().map(|(_, k)| kind(k)).collect(); + let type_parameters = f.type_formals.iter().map(|(_, k)| kind(k)).collect(); Ok(vm::file_format::FunctionSignature { - return_types, - arg_types, - type_formals, + return_, + parameters, + type_parameters, }) } @@ -646,9 +689,8 @@ fn compile_structs( context: &mut Context, self_name: &ModuleName, structs: Vec, -) -> Result<(Vec, Vec)> { +) -> Result> { let mut struct_defs = vec![]; - let mut field_defs = vec![]; for s in structs { let sident = QualifiedStructIdent { module: self_name.clone(), @@ -658,48 +700,38 @@ fn compile_structs( record_src_loc!(struct_decl: context, s.loc); record_src_loc!(struct_type_formals: context, &s.value.type_formals); let m = type_parameter_indexes(&s.value.type_formals)?; - let field_information = - compile_fields(context, &m, &mut field_defs, sh_idx, s.value.fields)?; - context.declare_struct_definition_index(s.value.name)?; + let sd_idx = context.declare_struct_definition_index(s.value.name)?; + let field_information = compile_fields(context, &m, sh_idx, sd_idx, s.value.fields)?; struct_defs.push(StructDefinition { struct_handle: sh_idx, field_information, }); } - Ok((struct_defs, field_defs)) + Ok(struct_defs) } fn compile_fields( context: &mut Context, type_parameters: &HashMap, - field_pool: &mut Vec, sh_idx: StructHandleIndex, + sd_idx: StructDefinitionIndex, sfields: StructDefinitionFields, ) -> Result { Ok(match sfields { StructDefinitionFields::Native => StructFieldInformation::Native, StructDefinitionFields::Move { fields } => { - let pool_len = field_pool.len(); - let field_count = fields.len(); - - let field_information = StructFieldInformation::Declared { - field_count: (field_count as MemberCount), - fields: FieldDefinitionIndex(pool_len as TableIndex), - }; - + let mut decl_fields = vec![]; for (decl_order, (f, ty)) in fields.into_iter().enumerate() { let name = context.identifier_index(f.value.as_inner())?; - record_src_loc!(field: context, f); + record_src_loc!(field: context, sd_idx, f); let sig_token = compile_type(context, type_parameters, &ty)?; - let signature = context.type_signature_index(sig_token.clone())?; - context.declare_field(sh_idx, f.value, sig_token, decl_order)?; - field_pool.push(FieldDefinition { - struct_: sh_idx, + context.declare_field(sh_idx, sd_idx, f.value, sig_token.clone(), decl_order); + decl_fields.push(FieldDefinition { name, - signature, + signature: TypeSignature(sig_token), }); } - field_information + StructFieldInformation::Declared(decl_fields) } }) } @@ -787,7 +819,7 @@ fn compile_function_body( block: Block_, ) -> Result { let mut function_frame = FunctionFrame::new(type_parameters); - let mut locals_signature = LocalsSignature(vec![]); + let mut locals_signature = Signature(vec![]); for (var, t) in formals { let sig = compile_type(context, function_frame.type_parameters(), &t)?; function_frame.define_local(&var.value, sig.clone())?; @@ -800,7 +832,7 @@ fn compile_function_body( locals_signature.0.push(sig); record_src_loc!(local: context, var_); } - let sig_idx = context.locals_signature_index(locals_signature)?; + let sig_idx = context.signature_index(locals_signature)?; let mut code = vec![]; compile_block(context, &mut function_frame, &mut code, block)?; @@ -1004,17 +1036,22 @@ fn compile_command( compile_lvalues(context, function_frame, code, lvalues)?; } Cmd_::Unpack(name, tys, bindings, e) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; compile_expression(context, function_frame, code, *e)?; let def_idx = context.struct_definition_index(&name)?; - push_instr!(cmd.loc, Bytecode::Unpack(def_idx, type_actuals_id)); + if tys.is_empty() { + push_instr!(cmd.loc, Bytecode::Unpack(def_idx)); + } else { + let type_parameters_id = context.signature_index(tokens)?; + let si_idx = context.struct_instantiation_index(def_idx, type_parameters_id)?; + push_instr!(cmd.loc, Bytecode::UnpackGeneric(si_idx)); + } function_frame.pop()?; for (field_, lhs_variable) in bindings.iter().rev() { @@ -1174,8 +1211,8 @@ fn compile_expression( Exp_::Pack(name, ast_tys, fields) => { let sig_tys = compile_types(context, function_frame.type_parameters(), &ast_tys)?; let tys = InferredType::from_signature_tokens(&sig_tys); - let tokens = LocalsSignature(sig_tys); - let type_actuals_id = context.locals_signature_index(tokens)?; + let tokens = Signature(sig_tys); + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&name)?; let self_name = ModuleName::new(ModuleName::self_name().into()); @@ -1195,7 +1232,12 @@ fn compile_expression( compile_expression(context, function_frame, code, e)?; } - push_instr!(exp.loc, Bytecode::Pack(def_idx, type_actuals_id)); + if tys.is_empty() { + push_instr!(exp.loc, Bytecode::Pack(def_idx)); + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + push_instr!(exp.loc, Bytecode::PackGeneric(si_idx)); + } for _ in 0..num_fields { function_frame.pop()?; } @@ -1315,19 +1357,35 @@ fn compile_expression( loc_type_opt.ok_or_else(|| format_err!("Impossible no expression to borrow"))?; let (sh_idx, tys) = loc_type.get_struct_handle()?; let subst = make_type_argument_subst(tys)?; - let (fd_idx, field_type, _) = context.field(sh_idx, field)?; + let (def_idx, field_type, field_offset) = context.field(sh_idx, field)?; + function_frame.pop()?; let inner_token = Box::new(InferredType::from_signature_token_with_subst( &subst, &field_type, )); + + let fh_idx = context.field_handle_index(def_idx, field_offset as u16)?; + if tys.is_empty() { + if is_mutable { + push_instr!(exp.loc, Bytecode::MutBorrowField(fh_idx)); + } else { + push_instr!(exp.loc, Bytecode::ImmBorrowField(fh_idx)); + } + } else { + let inst = InferredType::build_signature_tokens(tys)?; + let inst_idx = context.signature_index(Signature(inst))?; + let field_inst_idx = context.field_instantiation_index(fh_idx, inst_idx)?; + if is_mutable { + push_instr!(exp.loc, Bytecode::MutBorrowFieldGeneric(field_inst_idx)); + } else { + push_instr!(exp.loc, Bytecode::ImmBorrowFieldGeneric(field_inst_idx)); + } + }; + function_frame.push()?; if is_mutable { - push_instr!(exp.loc, Bytecode::MutBorrowField(fd_idx)); - function_frame.push()?; vec_deque![InferredType::MutableReference(inner_token)] } else { - push_instr!(exp.loc, Bytecode::ImmBorrowField(fd_idx)); - function_frame.push()?; vec_deque![InferredType::Reference(inner_token)] } } @@ -1365,14 +1423,20 @@ fn compile_call( vec_deque![InferredType::Address] } Builtin::Exists(name, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&name)?; - push_instr!(call.loc, Bytecode::Exists(def_idx, type_actuals_id)); + if tys.is_empty() { + push_instr!(call.loc, Bytecode::Exists(def_idx)); + } else { + let si_idx = + context.struct_instantiation_index(def_idx, type_actuals_id)?; + push_instr!(call.loc, Bytecode::ExistsGeneric(si_idx)); + } function_frame.pop()?; function_frame.push()?; vec_deque![InferredType::Bool] @@ -1381,16 +1445,28 @@ fn compile_call( let sig_tys = compile_types(context, function_frame.type_parameters(), &ast_tys)?; let tys = InferredType::from_signature_tokens(&sig_tys); - let tokens = LocalsSignature(sig_tys); - let type_actuals_id = context.locals_signature_index(tokens)?; + let tokens = Signature(sig_tys); + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&name)?; - push_instr! {call.loc, - if mut_ { - Bytecode::MutBorrowGlobal(def_idx, type_actuals_id) - } else { - Bytecode::ImmBorrowGlobal(def_idx, type_actuals_id) - } - }; + if tys.is_empty() { + push_instr! {call.loc, + if mut_ { + Bytecode::MutBorrowGlobal(def_idx) + } else { + Bytecode::ImmBorrowGlobal(def_idx) + } + }; + } else { + let si_idx = + context.struct_instantiation_index(def_idx, type_actuals_id)?; + push_instr! {call.loc, + if mut_ { + Bytecode::MutBorrowGlobalGeneric(si_idx) + } else { + Bytecode::ImmBorrowGlobalGeneric(si_idx) + } + }; + } function_frame.pop()?; function_frame.push()?; @@ -1411,10 +1487,16 @@ fn compile_call( let sig_tys = compile_types(context, function_frame.type_parameters(), &ast_tys)?; let tys = InferredType::from_signature_tokens(&sig_tys); - let tokens = LocalsSignature(sig_tys); - let type_actuals_id = context.locals_signature_index(tokens)?; + let tokens = Signature(sig_tys); + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&name)?; - push_instr!(call.loc, Bytecode::MoveFrom(def_idx, type_actuals_id)); + if tys.is_empty() { + push_instr!(call.loc, Bytecode::MoveFrom(def_idx)); + } else { + let si_idx = + context.struct_instantiation_index(def_idx, type_actuals_id)?; + push_instr!(call.loc, Bytecode::MoveFromGeneric(si_idx)); + } function_frame.pop()?; // pop the address function_frame.push()?; // push the return value @@ -1427,15 +1509,20 @@ fn compile_call( vec_deque![InferredType::Struct(sh_idx, tys)] } Builtin::MoveToSender(name, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&name)?; - - push_instr!(call.loc, Bytecode::MoveToSender(def_idx, type_actuals_id)); + if tys.is_empty() { + push_instr!(call.loc, Bytecode::MoveToSender(def_idx)); + } else { + let si_idx = + context.struct_instantiation_index(def_idx, type_actuals_id)?; + push_instr!(call.loc, Bytecode::MoveToSenderGeneric(si_idx)); + } function_frame.push()?; vec_deque![] } @@ -1483,19 +1570,24 @@ fn compile_call( .map(|t| InferredType::from_signature_token(t)) .collect::>(); let subst = &make_type_argument_subst(&ty_args)?; - let tokens = LocalsSignature(ty_arg_tokens); - let type_actuals_id = context.locals_signature_index(tokens)?; + let tokens = Signature(ty_arg_tokens); + let type_actuals_id = context.signature_index(tokens)?; let fh_idx = context.function_handle(module.clone(), name.clone())?.1; - let fcall = Bytecode::Call(fh_idx, type_actuals_id); + let fcall = if type_actuals.is_empty() { + Bytecode::Call(fh_idx) + } else { + let fi_idx = context.function_instantiation_index(fh_idx, type_actuals_id)?; + Bytecode::CallGeneric(fi_idx) + }; push_instr!(call.loc, fcall); for _ in 0..argument_types.len() { function_frame.pop()?; } // Return value of current function is pushed onto the stack. function_frame.push()?; - let signature = &context.function_signature(module, name)?.0; + let signature = context.function_signature(module, name)?; signature - .return_types + .return_ .iter() .map(|t| InferredType::from_signature_token_with_subst(subst, t)) .collect() @@ -1515,7 +1607,7 @@ fn compile_function_body_bytecode( blocks: BytecodeBlocks, ) -> Result { let mut function_frame = FunctionFrame::new(type_parameters); - let mut locals_signature = LocalsSignature(vec![]); + let mut locals_signature = Signature(vec![]); for (var, t) in formals { let sig = compile_type(context, function_frame.type_parameters(), &t)?; function_frame.define_local(&var.value, sig.clone())?; @@ -1528,7 +1620,7 @@ fn compile_function_body_bytecode( locals_signature.0.push(sig); record_src_loc!(local: context, var_); } - let sig_idx = context.locals_signature_index(locals_signature)?; + let sig_idx = context.signature_index(locals_signature)?; let mut code = vec![]; let mut label_to_index: HashMap = HashMap::new(); @@ -1592,34 +1684,49 @@ fn compile_bytecode( IRBytecode_::MoveLoc(sp!(_, v_)) => Bytecode::MoveLoc(function_frame.get_local(&v_)?), IRBytecode_::StLoc(sp!(_, v_)) => Bytecode::StLoc(function_frame.get_local(&v_)?), IRBytecode_::Call(m, n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let fh_idx = context.function_handle(m, n)?.1; - Bytecode::Call(fh_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::Call(fh_idx) + } else { + let fi_idx = context.function_instantiation_index(fh_idx, type_actuals_id)?; + Bytecode::CallGeneric(fi_idx) + } } IRBytecode_::Pack(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::Pack(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::Pack(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::PackGeneric(si_idx) + } } IRBytecode_::Unpack(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::Unpack(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::Unpack(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::UnpackGeneric(si_idx) + } } IRBytecode_::ReadRef => Bytecode::ReadRef, IRBytecode_::WriteRef => Bytecode::WriteRef, @@ -1630,43 +1737,79 @@ fn compile_bytecode( IRBytecode_::ImmBorrowLoc(sp!(_, v_)) => { Bytecode::ImmBorrowLoc(function_frame.get_local(&v_)?) } - IRBytecode_::MutBorrowField(name, _tys, sp!(_, field_)) => { + IRBytecode_::MutBorrowField(name, tys, sp!(_, field_)) => { let qualified_struct_name = QualifiedStructIdent { module: ModuleName::module_self(), name, }; let sh_idx = context.struct_handle_index(qualified_struct_name)?; - let (fd_idx, _, _) = context.field(sh_idx, field_)?; - Bytecode::MutBorrowField(fd_idx) + let (def_idx, _, field_offset) = context.field(sh_idx, field_)?; + + let fh_idx = context.field_handle_index(def_idx, field_offset as u16)?; + if tys.is_empty() { + Bytecode::MutBorrowField(fh_idx) + } else { + let tokens = Signature(compile_types( + context, + function_frame.type_parameters(), + &tys, + )?); + let type_actuals_id = context.signature_index(tokens)?; + let fi_idx = context.field_instantiation_index(fh_idx, type_actuals_id)?; + Bytecode::MutBorrowFieldGeneric(fi_idx) + } } - IRBytecode_::ImmBorrowField(name, _tys, sp!(_, field_)) => { + IRBytecode_::ImmBorrowField(name, tys, sp!(_, field_)) => { let qualified_struct_name = QualifiedStructIdent { module: ModuleName::module_self(), name, }; let sh_idx = context.struct_handle_index(qualified_struct_name)?; - let (fd_idx, _, _) = context.field(sh_idx, field_)?; - Bytecode::ImmBorrowField(fd_idx) + let (def_idx, _, field_offset) = context.field(sh_idx, field_)?; + + let fh_idx = context.field_handle_index(def_idx, field_offset as u16)?; + if tys.is_empty() { + Bytecode::ImmBorrowField(fh_idx) + } else { + let tokens = Signature(compile_types( + context, + function_frame.type_parameters(), + &tys, + )?); + let type_actuals_id = context.signature_index(tokens)?; + let fi_idx = context.field_instantiation_index(fh_idx, type_actuals_id)?; + Bytecode::ImmBorrowFieldGeneric(fi_idx) + } } IRBytecode_::MutBorrowGlobal(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::MutBorrowGlobal(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::MutBorrowGlobal(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::MutBorrowGlobalGeneric(si_idx) + } } IRBytecode_::ImmBorrowGlobal(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::ImmBorrowGlobal(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::ImmBorrowGlobal(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::ImmBorrowGlobalGeneric(si_idx) + } } IRBytecode_::Add => Bytecode::Add, IRBytecode_::Sub => Bytecode::Sub, @@ -1688,34 +1831,49 @@ fn compile_bytecode( IRBytecode_::Abort => Bytecode::Abort, IRBytecode_::GetTxnSenderAddress => Bytecode::GetTxnSenderAddress, IRBytecode_::Exists(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::Exists(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::Exists(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::ExistsGeneric(si_idx) + } } IRBytecode_::MoveFrom(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::MoveFrom(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::MoveFrom(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::MoveFromGeneric(si_idx) + } } IRBytecode_::MoveToSender(n, tys) => { - let tokens = LocalsSignature(compile_types( + let tokens = Signature(compile_types( context, function_frame.type_parameters(), &tys, )?); - let type_actuals_id = context.locals_signature_index(tokens)?; + let type_actuals_id = context.signature_index(tokens)?; let def_idx = context.struct_definition_index(&n)?; - Bytecode::MoveToSender(def_idx, type_actuals_id) + if tys.is_empty() { + Bytecode::MoveToSender(def_idx) + } else { + let si_idx = context.struct_instantiation_index(def_idx, type_actuals_id)?; + Bytecode::MoveToSenderGeneric(si_idx) + } } IRBytecode_::Shl => Bytecode::Shl, IRBytecode_::Shr => Bytecode::Shr, diff --git a/language/compiler/ir-to-bytecode/src/context.rs b/language/compiler/ir-to-bytecode/src/context.rs index 9bdb0d81a387..df5bd57c93d5 100644 --- a/language/compiler/ir-to-bytecode/src/context.rs +++ b/language/compiler/ir-to-bytecode/src/context.rs @@ -10,11 +10,12 @@ use std::{clone::Clone, collections::HashMap, hash::Hash}; use vm::{ access::ModuleAccess, file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, CodeOffset, FieldDefinitionIndex, - FunctionDefinitionIndex, FunctionHandle, FunctionHandleIndex, FunctionSignature, - FunctionSignatureIndex, IdentifierIndex, Kind, LocalsSignature, LocalsSignatureIndex, - ModuleHandle, ModuleHandleIndex, SignatureToken, StructDefinitionIndex, StructHandle, - StructHandleIndex, TableIndex, TypeSignature, TypeSignatureIndex, + AddressPoolIndex, ByteArrayPoolIndex, CodeOffset, FieldHandle, FieldHandleIndex, + FieldInstantiation, FieldInstantiationIndex, FunctionDefinitionIndex, FunctionHandle, + FunctionHandleIndex, FunctionInstantiation, FunctionInstantiationIndex, FunctionSignature, + IdentifierIndex, Kind, ModuleHandle, ModuleHandleIndex, Signature, SignatureIndex, + SignatureToken, StructDefInstantiation, StructDefInstantiationIndex, StructDefinitionIndex, + StructHandle, StructHandleIndex, TableIndex, }, }; @@ -57,9 +58,10 @@ struct CompiledDependency<'a> { module_pool: &'a [ModuleHandle], struct_pool: &'a [StructHandle], - function_signatuire_pool: &'a [FunctionSignature], + function_pool: &'a [FunctionHandle], identifiers: &'a [Identifier], address_pool: &'a [AccountAddress], + signature_pool: &'a [Signature], } impl<'a> CompiledDependency<'a> { @@ -81,10 +83,11 @@ impl<'a> CompiledDependency<'a> { let defined_function_handles = dep .function_handles() .iter() - .filter(|fhandle| fhandle.module.0 == 0); - for fhandle in defined_function_handles { + .enumerate() + .filter(|(_idx, fhandle)| fhandle.module.0 == 0); + for (idx, fhandle) in defined_function_handles { let fname = dep.identifier_at(fhandle.name); - functions.insert(fname, fhandle.signature.0); + functions.insert(fname, idx as u16); } Ok(Self { @@ -92,9 +95,10 @@ impl<'a> CompiledDependency<'a> { functions, module_pool: dep.module_handles(), struct_pool: dep.struct_handles(), - function_signatuire_pool: dep.function_signatures(), + function_pool: dep.function_handles(), identifiers: dep.identifiers(), address_pool: dep.address_pool(), + signature_pool: dep.signatures(), }) } @@ -128,10 +132,17 @@ impl<'a> CompiledDependency<'a> { .and_then(|idx| self.struct_pool.get(*idx as usize)) } - fn function_signature(&self, name: &FunctionName) -> Option<&'a FunctionSignature> { + fn function_signature(&self, name: &FunctionName) -> Option { self.functions .get(ident_str(name.as_inner()).ok()?) - .and_then(|idx| self.function_signatuire_pool.get(*idx as usize)) + .and_then(|idx| { + let fh = self.function_pool.get(*idx as usize)?; + Some(FunctionSignature { + parameters: self.signature_pool[fh.parameters.0 as usize].0.clone(), + return_: self.signature_pool[fh.return_.0 as usize].0.clone(), + type_parameters: fh.type_parameters.clone(), + }) + }) } } @@ -144,12 +155,16 @@ pub struct MaterializedPools { pub struct_handles: Vec, /// Function handle pool pub function_handles: Vec, - /// Type signature pool - pub type_signatures: Vec, - /// Function signature pool - pub function_signatures: Vec, + /// Field handle pool + pub field_handles: Vec, + /// Struct instantiation pool + pub struct_def_instantiations: Vec, + /// Function instantiation pool + pub function_instantiations: Vec, + /// Field instantiation pool + pub field_instantiations: Vec, /// Locals signatures pool - pub locals_signatures: Vec, + pub signatures: Vec, /// Identifier pool pub identifiers: Vec, /// Byte array pool @@ -173,20 +188,23 @@ pub struct Context<'a> { labels: HashMap, // queryable pools - fields: HashMap<(StructHandleIndex, Field_), (TableIndex, SignatureToken, usize)>, + // TODO: lookup for Fields is not that seemless after binary format changes + // We need multiple lookups or a better representation for fields + fields: HashMap<(StructHandleIndex, Field_), (StructDefinitionIndex, SignatureToken, usize)>, function_handles: HashMap<(ModuleName, FunctionName), (FunctionHandle, FunctionHandleIndex)>, - function_signatures: - HashMap<(ModuleName, FunctionName), (FunctionSignature, FunctionSignatureIndex)>, + function_signatures: HashMap<(ModuleName, FunctionName), FunctionSignature>, // Simple pools - function_signature_pool: HashMap, module_handles: HashMap, struct_handles: HashMap, - type_signatures: HashMap, - locals_signatures: HashMap, + signatures: HashMap, identifiers: HashMap, byte_array_pool: HashMap, TableIndex>, address_pool: HashMap, + field_handles: HashMap, + struct_instantiations: HashMap, + function_instantiations: HashMap, + field_instantiations: HashMap, // The current function index that we are on current_function_index: FunctionDefinitionIndex, @@ -223,11 +241,13 @@ impl<'a> Context<'a> { fields: HashMap::new(), function_handles: HashMap::new(), function_signatures: HashMap::new(), - function_signature_pool: HashMap::new(), module_handles: HashMap::new(), struct_handles: HashMap::new(), - type_signatures: HashMap::new(), - locals_signatures: HashMap::new(), + field_handles: HashMap::new(), + struct_instantiations: HashMap::new(), + function_instantiations: HashMap::new(), + field_instantiations: HashMap::new(), + signatures: HashMap::new(), identifiers: HashMap::new(), byte_array_pool: HashMap::new(), address_pool: HashMap::new(), @@ -268,14 +288,16 @@ impl<'a> Context<'a> { ); let materialized_pools = MaterializedPools { function_handles, - function_signatures: Self::materialize_map(self.function_signature_pool), module_handles: Self::materialize_map(self.module_handles), struct_handles: Self::materialize_map(self.struct_handles), - type_signatures: Self::materialize_map(self.type_signatures), - locals_signatures: Self::materialize_map(self.locals_signatures), + field_handles: Self::materialize_map(self.field_handles), + signatures: Self::materialize_map(self.signatures), identifiers: Self::materialize_map(self.identifiers), byte_array_pool: Self::materialize_map(self.byte_array_pool), address_pool: Self::materialize_map(self.address_pool), + function_instantiations: Self::materialize_map(self.function_instantiations), + struct_def_instantiations: Self::materialize_map(self.struct_instantiations), + field_instantiations: Self::materialize_map(self.field_instantiations), }; (materialized_pools, self.source_map) } @@ -328,6 +350,67 @@ impl<'a> Context<'a> { )) } + /// Get the field handle index for the alias, adds it if missing. + pub fn field_handle_index( + &mut self, + owner: StructDefinitionIndex, + field: u16, + ) -> Result { + let field_handle = FieldHandle { owner, field }; + Ok(FieldHandleIndex(get_or_add_item( + &mut self.field_handles, + field_handle, + )?)) + } + + /// Get the struct instantiation index for the alias, adds it if missing. + pub fn struct_instantiation_index( + &mut self, + def: StructDefinitionIndex, + type_parameters: SignatureIndex, + ) -> Result { + let struct_inst = StructDefInstantiation { + def, + type_parameters, + }; + Ok(StructDefInstantiationIndex(get_or_add_item( + &mut self.struct_instantiations, + struct_inst, + )?)) + } + + /// Get the function instantiation index for the alias, adds it if missing. + pub fn function_instantiation_index( + &mut self, + handle: FunctionHandleIndex, + type_parameters: SignatureIndex, + ) -> Result { + let func_inst = FunctionInstantiation { + handle, + type_parameters, + }; + Ok(FunctionInstantiationIndex(get_or_add_item( + &mut self.function_instantiations, + func_inst, + )?)) + } + + /// Get the field instantiation index for the alias, adds it if missing. + pub fn field_instantiation_index( + &mut self, + handle: FieldHandleIndex, + type_parameters: SignatureIndex, + ) -> Result { + let field_inst = FieldInstantiation { + handle, + type_parameters, + }; + Ok(FieldInstantiationIndex(get_or_add_item( + &mut self.field_instantiations, + field_inst, + )?)) + } + /// Get the fake offset for the label. Labels will be fixed to real offsets after compilation pub fn label_index(&mut self, label: BlockLabel) -> Result { Ok(get_or_add_item(&mut self.labels, label)?) @@ -363,23 +446,13 @@ impl<'a> Context<'a> { &self, s: StructHandleIndex, f: Field_, - ) -> Result<(FieldDefinitionIndex, SignatureToken, usize)> { + ) -> Result<(StructDefinitionIndex, SignatureToken, usize)> { match self.fields.get(&(s, f.clone())) { None => bail!("Unbound field {}", f), - Some((idx, token, decl_order)) => { - Ok((FieldDefinitionIndex(*idx), token.clone(), *decl_order)) - } + Some((sd_idx, token, decl_order)) => Ok((*sd_idx, token.clone(), *decl_order)), } } - /// Get the type signature index, adds it if it is not bound. - pub fn type_signature_index(&mut self, token: SignatureToken) -> Result { - Ok(TypeSignatureIndex(get_or_add_item( - &mut self.type_signatures, - TypeSignature(token), - )?)) - } - /// Get the struct definition index, fails if it is not bound. pub fn struct_definition_index(&self, s: &StructName) -> Result { match self.struct_defs.get(&s) { @@ -388,15 +461,9 @@ impl<'a> Context<'a> { } } - /// Get the locals signature pool index, adds it if missing. - pub fn locals_signature_index( - &mut self, - locals: LocalsSignature, - ) -> Result { - Ok(LocalsSignatureIndex(get_or_add_item( - &mut self.locals_signatures, - locals, - )?)) + /// Get the signature pool index, adds it if missing. + pub fn signature_index(&mut self, sig: Signature) -> Result { + Ok(SignatureIndex(get_or_add_item(&mut self.signatures, sig)?)) } pub fn set_function_index(&mut self, index: TableIndex) { @@ -440,7 +507,7 @@ impl<'a> Context<'a> { &mut self, sname: QualifiedStructIdent, is_nominal_resource: bool, - type_formals: Vec, + type_parameters: Vec, ) -> Result { let module = self.module_handle_index(&sname.module)?; let name = self.identifier_index(sname.name.as_inner())?; @@ -450,7 +517,7 @@ impl<'a> Context<'a> { module, name, is_nominal_resource, - type_formals, + type_parameters, }, ); Ok(StructHandleIndex(get_or_add_item_ref( @@ -488,15 +555,24 @@ impl<'a> Context<'a> { let module = self.module_handle_index(&mname)?; let name = self.identifier_index(fname.as_inner())?; - let sidx = get_or_add_item_ref(&mut self.function_signature_pool, &signature)?; - let signature_index = FunctionSignatureIndex(sidx as TableIndex); self.function_signatures - .insert(m_f.clone(), (signature, signature_index)); + .insert(m_f.clone(), signature.clone()); + + let FunctionSignature { + return_, + parameters, + type_parameters, + } = signature; + + let params_idx = get_or_add_item(&mut self.signatures, Signature(parameters))?; + let return_idx = get_or_add_item(&mut self.signatures, Signature(return_))?; let handle = FunctionHandle { module, name, - signature: signature_index, + parameters: SignatureIndex(params_idx as TableIndex), + return_: SignatureIndex(return_idx as TableIndex), + type_parameters, }; // handle duplicate declarations // erroring on duplicates needs to be done by the bytecode verifier @@ -517,21 +593,15 @@ impl<'a> Context<'a> { pub fn declare_field( &mut self, s: StructHandleIndex, + sd_idx: StructDefinitionIndex, f: Field_, token: SignatureToken, decl_order: usize, - ) -> Result { - let idx = self.fields.len(); - if idx > TABLE_MAX_SIZE { - bail!("too many fields: {}.{}", s, f) - } + ) { // need to handle duplicates - Ok(FieldDefinitionIndex( - self.fields - .entry((s, f)) - .or_insert((idx as TableIndex, token, decl_order)) - .0, - )) + self.fields + .entry((s, f)) + .or_insert((sd_idx, token, decl_order)); } //********************************************************************************************** @@ -552,7 +622,7 @@ impl<'a> Context<'a> { let dep = self.dependency(&mident)?; match dep.struct_handle(s) { None => bail!("Unbound struct {}", s), - Some(shandle) => Ok((shandle.is_nominal_resource, shandle.type_formals.clone())), + Some(shandle) => Ok((shandle.is_nominal_resource, shandle.type_parameters.clone())), } } @@ -563,8 +633,8 @@ impl<'a> Context<'a> { match self.structs.get(&s) { Some(sh) => Ok(StructHandleIndex(*self.struct_handles.get(sh).unwrap())), None => { - let (is_nominal_resource, type_formals) = self.dep_struct_handle(&s)?; - self.declare_struct_handle_index(s, is_nominal_resource, type_formals) + let (is_nominal_resource, type_parameters) = self.dep_struct_handle(&s)?; + self.declare_struct_handle_index(s, is_nominal_resource, type_parameters) } } } @@ -593,7 +663,20 @@ impl<'a> Context<'a> { let correct_inner = self.reindex_signature_token(dep, *inner)?; SignatureToken::MutableReference(Box::new(correct_inner)) } - SignatureToken::Struct(orig_sh_idx, inners) => { + SignatureToken::Struct(orig_sh_idx) => { + let dep_info = self.dependency(&dep)?; + let (mident, sname) = dep_info + .source_struct_info(orig_sh_idx) + .ok_or_else(|| format_err!("Malformed dependency"))?; + let module_name = self.module_alias(&mident)?.clone(); + let sident = QualifiedStructIdent { + module: module_name, + name: sname, + }; + let correct_sh_idx = self.struct_handle_index(sident)?; + SignatureToken::Struct(correct_sh_idx) + } + SignatureToken::StructInstantiation(orig_sh_idx, inners) => { let dep_info = self.dependency(&dep)?; let (mident, sname) = dep_info .source_struct_info(orig_sh_idx) @@ -608,7 +691,7 @@ impl<'a> Context<'a> { .into_iter() .map(|t| self.reindex_signature_token(dep, t)) .collect::>()?; - SignatureToken::Struct(correct_sh_idx, correct_inners) + SignatureToken::StructInstantiation(correct_sh_idx, correct_inners) } }) } @@ -618,21 +701,21 @@ impl<'a> Context<'a> { dep: &QualifiedModuleIdent, orig: FunctionSignature, ) -> Result { - let return_types = orig - .return_types + let return_ = orig + .return_ .into_iter() .map(|t| self.reindex_signature_token(dep, t)) .collect::>()?; - let arg_types = orig - .arg_types + let parameters = orig + .parameters .into_iter() .map(|t| self.reindex_signature_token(dep, t)) .collect::>()?; - let type_formals = orig.type_formals; + let type_parameters = orig.type_parameters; Ok(vm::file_format::FunctionSignature { - return_types, - arg_types, - type_formals, + return_, + parameters, + type_parameters, }) } @@ -646,7 +729,7 @@ impl<'a> Context<'a> { } let mident = self.module_ident(m)?.clone(); let dep = self.dependency(&mident)?; - match dep.function_signature(f).cloned() { + match dep.function_signature(f) { None => bail!("Unbound function {}.{}", m, f), Some(sig) => self.reindex_function_signature(&mident, sig), } @@ -684,7 +767,7 @@ impl<'a> Context<'a> { &mut self, m: ModuleName, f: FunctionName, - ) -> Result<&(FunctionSignature, FunctionSignatureIndex)> { + ) -> Result<&FunctionSignature> { self.ensure_function_declared(m.clone(), f.clone())?; Ok(self.function_signatures.get(&(m, f)).unwrap()) } diff --git a/language/compiler/ir-to-bytecode/syntax/src/lexer.rs b/language/compiler/ir-to-bytecode/syntax/src/lexer.rs index 3dd476c7c8e4..07d3a7534d59 100644 --- a/language/compiler/ir-to-bytecode/syntax/src/lexer.rs +++ b/language/compiler/ir-to-bytecode/syntax/src/lexer.rs @@ -102,7 +102,7 @@ pub enum Tok { U64, U128, Vector, - Unrestricted, + Copyable, While, LBrace, Pipe, @@ -455,7 +455,7 @@ fn get_name_token(name: &str) -> Tok { "u8" => Tok::U8, "u64" => Tok::U64, "u128" => Tok::U128, - "unrestricted" => Tok::Unrestricted, + "copyable" => Tok::Copyable, "while" => Tok::While, _ => Tok::NameValue, } diff --git a/language/compiler/ir-to-bytecode/syntax/src/syntax.rs b/language/compiler/ir-to-bytecode/syntax/src/syntax.rs index 2cd14ac41cb3..733cd9cb60e1 100644 --- a/language/compiler/ir-to-bytecode/syntax/src/syntax.rs +++ b/language/compiler/ir-to-bytecode/syntax/src/syntax.rs @@ -1130,13 +1130,13 @@ fn parse_function_block_<'input>( // Kind: Kind = { // "resource" => Kind::Resource, -// "unrestricted" => Kind::Unrestricted, +// "copyable" => Kind::Copyable, // } fn parse_kind<'input>(tokens: &mut Lexer<'input>) -> Result> { let k = match tokens.peek() { Tok::Resource => Kind::Resource, - Tok::Unrestricted => Kind::Unrestricted, + Tok::Copyable => Kind::Copyable, _ => { return Err(ParseError::InvalidToken { location: current_token_loc(tokens), @@ -1229,7 +1229,7 @@ fn parse_type_var<'input>( // > )?> =>? { // } -fn parse_type_formal<'input>( +fn parse_type_parameter<'input>( tokens: &mut Lexer<'input>, ) -> Result<(TypeVar, Kind), ParseError> { let type_var = parse_type_var(tokens)?; @@ -1265,7 +1265,7 @@ fn parse_type_actuals<'input>( // => (n, vec![]), // } -fn parse_name_and_type_formals<'input>( +fn parse_name_and_type_parameters<'input>( tokens: &mut Lexer<'input>, ) -> Result<(String, Vec<(TypeVar, Kind)>), ParseError> { let mut has_types = false; @@ -1276,7 +1276,7 @@ fn parse_name_and_type_formals<'input>( parse_name(tokens)? }; let k = if has_types { - let list = parse_comma_list(tokens, &[Tok::Greater], parse_type_formal, true)?; + let list = parse_comma_list(tokens, &[Tok::Greater], parse_type_parameter, true)?; consume_token(tokens, Tok::Greater)?; list } else { @@ -1713,14 +1713,14 @@ fn parse_synthetic_<'input>( // } // MoveFunctionDecl : (FunctionName, Function) = { -// "(" "(" ")" // // =>? { ... } // } // NativeFunctionDecl: (FunctionName, Function) = { -// +// // "(" > ")" // // ";" =>? { ... } @@ -1745,7 +1745,7 @@ fn parse_function_decl<'input>( false }; - let (name, type_formals) = parse_name_and_type_formals(tokens)?; + let (name, type_parameters) = parse_name_and_type_parameters(tokens)?; consume_token(tokens, Tok::LParen)?; let args = parse_comma_list(tokens, &[Tok::RParen], parse_arg_decl, true)?; consume_token(tokens, Tok::RParen)?; @@ -1780,7 +1780,7 @@ fn parse_function_decl<'input>( }, args, ret.unwrap_or_else(|| vec![]), - type_formals, + type_parameters, acquires.unwrap_or_else(Vec::new), specifications, if is_native { @@ -1828,7 +1828,7 @@ fn parse_script<'input>( consume_token(tokens, Tok::Main)?; let type_formals = if tokens.peek() == Tok::Less { consume_token(tokens, Tok::Less)?; - let list = parse_comma_list(tokens, &[Tok::Greater], parse_type_formal, true)?; + let list = parse_comma_list(tokens, &[Tok::Greater], parse_type_parameter, true)?; consume_token(tokens, Tok::Greater)?; list } else { @@ -1857,10 +1857,10 @@ fn parse_script<'input>( // "resource" => true // } // StructDecl: StructDefinition_ = { -// "{" > "}" =>? { ... } // -// ";" =>? { ... } +// ";" =>? { ... } // } fn parse_struct_decl<'input>( @@ -1886,7 +1886,7 @@ fn parse_struct_decl<'input>( }; tokens.advance()?; - let (name, type_formals) = parse_name_and_type_formals(tokens)?; + let (name, type_parameters) = parse_name_and_type_parameters(tokens)?; if is_native { consume_token(tokens, Tok::Semicolon)?; @@ -1895,7 +1895,7 @@ fn parse_struct_decl<'input>( tokens.file_name(), start_loc, end_loc, - StructDefinition_::native(is_nominal_resource, name, type_formals)?, + StructDefinition_::native(is_nominal_resource, name, type_parameters)?, )); } @@ -1920,7 +1920,7 @@ fn parse_struct_decl<'input>( StructDefinition_::move_declared( is_nominal_resource, name, - type_formals, + type_parameters, fields, invariants, )?, diff --git a/language/compiler/src/unit_tests/cfg_tests.rs b/language/compiler/src/unit_tests/cfg_tests.rs index ab9c1fe6babb..af3e78b57ad0 100644 --- a/language/compiler/src/unit_tests/cfg_tests.rs +++ b/language/compiler/src/unit_tests/cfg_tests.rs @@ -174,7 +174,7 @@ fn cfg_compile_if_else_with_if_return() { let compiled_script_res = compile_script_string(&code); let compiled_script = compiled_script_res.unwrap(); let cfg: VMControlFlowGraph = VMControlFlowGraph::new(&compiled_script.main().code.code); - println!("SCRIPT:\n {}", compiled_script); + println!("SCRIPT:\n {:?}", compiled_script); cfg.display(); assert_eq!(cfg.blocks().len(), 3); assert_eq!(cfg.num_blocks(), 3); @@ -200,7 +200,7 @@ fn cfg_compile_if_else_with_two_returns() { let compiled_script_res = compile_script_string(&code); let compiled_script = compiled_script_res.unwrap(); let cfg: VMControlFlowGraph = VMControlFlowGraph::new(&compiled_script.main().code.code); - println!("SCRIPT:\n {}", compiled_script); + println!("SCRIPT:\n {:?}", compiled_script); cfg.display(); assert_eq!(cfg.blocks().len(), 4); assert_eq!(cfg.num_blocks(), 4); @@ -254,7 +254,7 @@ fn cfg_compile_if_else_with_if_abort() { let compiled_script_res = compile_script_string(&code); let compiled_script = compiled_script_res.unwrap(); let cfg: VMControlFlowGraph = VMControlFlowGraph::new(&compiled_script.main().code.code); - println!("SCRIPT:\n {}", compiled_script); + println!("SCRIPT:\n {:?}", compiled_script); cfg.display(); assert_eq!(cfg.blocks().len(), 3); assert_eq!(cfg.num_blocks(), 3); @@ -280,7 +280,7 @@ fn cfg_compile_if_else_with_two_aborts() { let compiled_script_res = compile_script_string(&code); let compiled_script = compiled_script_res.unwrap(); let cfg: VMControlFlowGraph = VMControlFlowGraph::new(&compiled_script.main().code.code); - println!("SCRIPT:\n {}", compiled_script); + println!("SCRIPT:\n {:?}", compiled_script); cfg.display(); assert_eq!(cfg.blocks().len(), 4); assert_eq!(cfg.num_blocks(), 4); diff --git a/language/compiler/src/unit_tests/expression_tests.rs b/language/compiler/src/unit_tests/expression_tests.rs index e1d9674d2f1d..2400e05fc4a4 100644 --- a/language/compiler/src/unit_tests/expression_tests.rs +++ b/language/compiler/src/unit_tests/expression_tests.rs @@ -32,9 +32,7 @@ fn compile_script_expr_addition() { assert_eq!(compiled_script.main().code.code.len(), 9); assert!(compiled_script.struct_handles().is_empty()); assert_eq!(compiled_script.function_handles().len(), 1); - assert!(compiled_script.type_signatures().is_empty()); - assert_eq!(compiled_script.function_signatures().len(), 1); // method sig - assert_eq!(compiled_script.locals_signatures().len(), 1); // local variables sig + assert_eq!(compiled_script.signatures().len(), 2); assert_eq!(compiled_script.module_handles().len(), 1); // the module assert_eq!(compiled_script.identifiers().len(), 2); // the name of `main()` + the name of the "" module assert_eq!(compiled_script.address_pool().len(), 1); // the empty address of module @@ -62,9 +60,7 @@ fn compile_script_expr_combined() { assert_eq!(compiled_script.main().code.code.len(), 13); assert!(compiled_script.struct_handles().is_empty()); assert_eq!(compiled_script.function_handles().len(), 1); - assert!(compiled_script.type_signatures().is_empty()); - assert_eq!(compiled_script.function_signatures().len(), 1); // method sig - assert_eq!(compiled_script.locals_signatures().len(), 1); // local variables sig + assert_eq!(compiled_script.signatures().len(), 2); assert_eq!(compiled_script.module_handles().len(), 1); // the module assert_eq!(compiled_script.identifiers().len(), 2); // the name of `main()` + the name of the "" module assert_eq!(compiled_script.address_pool().len(), 1); // the empty address of module @@ -89,9 +85,7 @@ fn compile_script_borrow_local() { assert_eq!(count_locals(&compiled_script), 2); assert!(compiled_script.struct_handles().is_empty()); assert_eq!(compiled_script.function_handles().len(), 1); - assert!(compiled_script.type_signatures().is_empty()); - assert_eq!(compiled_script.function_signatures().len(), 1); // method sig - assert_eq!(compiled_script.locals_signatures().len(), 1); // local variables sig + assert_eq!(compiled_script.signatures().len(), 2); assert_eq!(compiled_script.module_handles().len(), 1); // the module assert_eq!(compiled_script.identifiers().len(), 2); // the name of `main()` + the name of the "" module assert_eq!(compiled_script.address_pool().len(), 1); // the empty address of module @@ -116,9 +110,7 @@ fn compile_script_borrow_local_mutable() { assert_eq!(count_locals(&compiled_script), 2); assert!(compiled_script.struct_handles().is_empty()); assert_eq!(compiled_script.function_handles().len(), 1); - assert!(compiled_script.type_signatures().is_empty()); - assert_eq!(compiled_script.function_signatures().len(), 1); // method sig - assert_eq!(compiled_script.locals_signatures().len(), 1); // local variables sig + assert_eq!(compiled_script.signatures().len(), 2); assert_eq!(compiled_script.module_handles().len(), 1); // the module assert_eq!(compiled_script.identifiers().len(), 2); // the name of `main()` + the name of the "" module assert_eq!(compiled_script.address_pool().len(), 1); // the empty address of module @@ -144,9 +136,7 @@ fn compile_script_borrow_reference() { assert_eq!(count_locals(&compiled_script), 3); assert!(compiled_script.struct_handles().is_empty()); assert_eq!(compiled_script.function_handles().len(), 1); - assert!(compiled_script.type_signatures().is_empty()); - assert_eq!(compiled_script.function_signatures().len(), 1); // method sig - assert_eq!(compiled_script.locals_signatures().len(), 1); // local variables sig + assert_eq!(compiled_script.signatures().len(), 2); assert_eq!(compiled_script.module_handles().len(), 1); // the module assert_eq!(compiled_script.identifiers().len(), 2); // the name of `main()` + the name of the "" module assert_eq!(compiled_script.address_pool().len(), 1); // the empty address of module @@ -239,3 +229,37 @@ fn compile_borrow_field() { let compiled_module_res = compile_module_string(&code); let _compiled_module = compiled_module_res.unwrap(); } + +#[test] +fn compile_borrow_field_generic() { + let code = String::from( + " + module Foobar { + resource FooCoin { value: u64 } + + public borrow_immut_field(arg: &Self.FooCoin) { + let field_ref: &u64; + field_ref = &move(arg).value; + _ = move(field_ref); + return; + } + + public borrow_immut_field_from_mut_ref(arg: &mut Self.FooCoin) { + let field_ref: &u64; + field_ref = &move(arg).value; + _ = move(field_ref); + return; + } + + public borrow_mut_field(arg: &mut Self.FooCoin
) { + let field_ref: &mut u64; + field_ref = &mut move(arg).value; + _ = move(field_ref); + return; + } + } + ", + ); + let compiled_module_res = compile_module_string(&code); + let _compiled_module = compiled_module_res.unwrap(); +} diff --git a/language/compiler/src/unit_tests/testutils.rs b/language/compiler/src/unit_tests/testutils.rs index c5f5096560f1..a0cd0cb23105 100644 --- a/language/compiler/src/unit_tests/testutils.rs +++ b/language/compiler/src/unit_tests/testutils.rs @@ -137,10 +137,7 @@ pub fn compile_module_string_and_assert_error( } pub fn count_locals(script: &CompiledScript) -> usize { - script - .locals_signature_at(script.main().code.locals) - .0 - .len() + script.signature_at(script.main().code.locals).0.len() } pub fn compile_module_string_with_stdlib(code: &str) -> Result { diff --git a/language/e2e-tests/src/tests/scripts.rs b/language/e2e-tests/src/tests/scripts.rs index 51a3fc440b4c..da8aebe4d216 100644 --- a/language/e2e-tests/src/tests/scripts.rs +++ b/language/e2e-tests/src/tests/scripts.rs @@ -8,8 +8,8 @@ use libra_types::{ }; use move_core_types::identifier::Identifier; use vm::file_format::{ - empty_script, AddressPoolIndex, Bytecode, FunctionHandle, FunctionHandleIndex, - FunctionSignatureIndex, IdentifierIndex, LocalsSignatureIndex, ModuleHandle, ModuleHandleIndex, + empty_script, AddressPoolIndex, Bytecode, FunctionHandle, FunctionHandleIndex, IdentifierIndex, + ModuleHandle, ModuleHandleIndex, SignatureIndex, }; #[test] @@ -81,15 +81,16 @@ fn script_none_existing_module_dep() { let fun_handle = FunctionHandle { module: ModuleHandleIndex((script.module_handles.len() - 1) as u16), name: IdentifierIndex((script.identifiers.len() - 1) as u16), - signature: FunctionSignatureIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }; script.function_handles.push(fun_handle); script.main.code.code = vec![ - Bytecode::Call( - FunctionHandleIndex((script.function_handles.len() - 1) as u16), - LocalsSignatureIndex(0), - ), + Bytecode::Call(FunctionHandleIndex( + (script.function_handles.len() - 1) as u16, + )), Bytecode::Ret, ]; let mut blob = vec![]; @@ -150,15 +151,16 @@ fn script_non_existing_function_dep() { let fun_handle = FunctionHandle { module: ModuleHandleIndex((script.module_handles.len() - 1) as u16), name: IdentifierIndex((script.identifiers.len() - 1) as u16), - signature: FunctionSignatureIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }; script.function_handles.push(fun_handle); script.main.code.code = vec![ - Bytecode::Call( - FunctionHandleIndex((script.function_handles.len() - 1) as u16), - LocalsSignatureIndex(0), - ), + Bytecode::Call(FunctionHandleIndex( + (script.function_handles.len() - 1) as u16, + )), Bytecode::Ret, ]; let mut blob = vec![]; @@ -221,15 +223,16 @@ fn script_bad_sig_function_dep() { let fun_handle = FunctionHandle { module: ModuleHandleIndex((script.module_handles.len() - 1) as u16), name: IdentifierIndex((script.identifiers.len() - 1) as u16), - signature: FunctionSignatureIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }; script.function_handles.push(fun_handle); script.main.code.code = vec![ - Bytecode::Call( - FunctionHandleIndex((script.function_handles.len() - 1) as u16), - LocalsSignatureIndex(0), - ), + Bytecode::Call(FunctionHandleIndex( + (script.function_handles.len() - 1) as u16, + )), Bytecode::Ret, ]; let mut blob = vec![]; diff --git a/language/ir-testsuite/tests/gas_schedule/gas_schedule_has_proper_number_of_instructions.mvir b/language/ir-testsuite/tests/gas_schedule/gas_schedule_has_proper_number_of_instructions.mvir index b3bc105a6e57..5b240482854e 100644 --- a/language/ir-testsuite/tests/gas_schedule/gas_schedule_has_proper_number_of_instructions.mvir +++ b/language/ir-testsuite/tests/gas_schedule/gas_schedule_has_proper_number_of_instructions.mvir @@ -1,7 +1,7 @@ import 0x0.GasSchedule; main() { - assert(GasSchedule.instruction_table_size() == 59, 0); - assert(GasSchedule.native_table_size() == 17, 0); + assert(GasSchedule.instruction_table_size() == 69, GasSchedule.instruction_table_size()); + assert(GasSchedule.native_table_size() == 17, GasSchedule.instruction_table_size()); return; } diff --git a/language/ir-testsuite/tests/generics/function_def.mvir b/language/ir-testsuite/tests/generics/function_def.mvir index bf5cd7768059..3e522512be50 100644 --- a/language/ir-testsuite/tests/generics/function_def.mvir +++ b/language/ir-testsuite/tests/generics/function_def.mvir @@ -7,7 +7,7 @@ module M { return move(x); } - public foo(x1: T4, x2: T3) { + public foo(x1: T4, x2: T3) { let x3: T2; let x4: T1; abort 0; diff --git a/language/ir-testsuite/tests/generics/gen_move_to.mvir b/language/ir-testsuite/tests/generics/gen_move_to.mvir index 5794188ca31c..aebfa6970e0f 100644 --- a/language/ir-testsuite/tests/generics/gen_move_to.mvir +++ b/language/ir-testsuite/tests/generics/gen_move_to.mvir @@ -8,7 +8,7 @@ module M { // a resource resource Value { val: u64 } - // a generic type either unrestricted or resource + // a generic type either copyable or resource struct Container { elem: T } // Functions @@ -19,8 +19,8 @@ module M { return Container{ elem: move(t) }; } - // change the elem of a `Container` if T is unrestricted - public change_container(c: &mut Self.Container, t: T) { + // change the elem of a `Container` if T is copyable + public change_container(c: &mut Self.Container, t: T) { *&mut move(c).elem = move(t); return; } @@ -112,7 +112,7 @@ module M { } // publish a `Some` under sender account - public change_some(val: T) acquires Some { + public change_some(val: T) acquires Some { let some: &mut Self.Some; some = borrow_global_mut>(get_txn_sender()); *(&mut move(some).v) = move(val); diff --git a/language/ir-testsuite/tests/generics/import_function.mvir b/language/ir-testsuite/tests/generics/import_function.mvir index 5f6cfc4e16b0..9f81e52a1cbb 100644 --- a/language/ir-testsuite/tests/generics/import_function.mvir +++ b/language/ir-testsuite/tests/generics/import_function.mvir @@ -5,7 +5,7 @@ module M { abort 0; } - public bar(x: T3, y: T2, z: T1) { + public bar(x: T3, y: T2, z: T1) { abort 0; } diff --git a/language/ir-testsuite/tests/generics/import_struct.mvir b/language/ir-testsuite/tests/generics/import_struct.mvir index 5ef904eb9058..4fbee79e6f84 100644 --- a/language/ir-testsuite/tests/generics/import_struct.mvir +++ b/language/ir-testsuite/tests/generics/import_struct.mvir @@ -1,7 +1,7 @@ module M { resource Foo { x: T } - struct Bar { x: T3, y: T2, z: T1 } + struct Bar { x: T3, y: T2, z: T1 } } //! new-transaction diff --git a/language/ir-testsuite/tests/generics/option.mvir b/language/ir-testsuite/tests/generics/option.mvir index c4454507544c..48f297573d7b 100644 --- a/language/ir-testsuite/tests/generics/option.mvir +++ b/language/ir-testsuite/tests/generics/option.mvir @@ -16,7 +16,7 @@ module Option { return T { v: move(v) }; } - public unwrap_or(x: Self.T, e: E): E { + public unwrap_or(x: Self.T, e: E): E { let v: vector; T { v: v } = move(x); if (Vector.is_empty(&v)) { @@ -25,7 +25,7 @@ module Option { return Vector.pop_back(&mut v); } - public really_none(): Self.T { + public really_none(): Self.T { return Self.unwrap_or>(Self.none>(), Self.none()); } } diff --git a/language/ir-testsuite/tests/generics/resource_safety/borrow_global_mut.mvir b/language/ir-testsuite/tests/generics/resource_safety/borrow_global_mut.mvir index 3ea70255f191..d77f11d1fcc0 100644 --- a/language/ir-testsuite/tests/generics/resource_safety/borrow_global_mut.mvir +++ b/language/ir-testsuite/tests/generics/resource_safety/borrow_global_mut.mvir @@ -1,5 +1,5 @@ module M { - struct Foo { x: T } + struct Foo { x: T } f(): u64 acquires Foo { let foo_ref: &mut Self.Foo; diff --git a/language/ir-testsuite/tests/generics/signatures/all_as_unrestricted.mvir b/language/ir-testsuite/tests/generics/signatures/all_as_unrestricted.mvir index dbfb49a97e87..2cc2248b2006 100644 --- a/language/ir-testsuite/tests/generics/signatures/all_as_unrestricted.mvir +++ b/language/ir-testsuite/tests/generics/signatures/all_as_unrestricted.mvir @@ -1,5 +1,5 @@ module M { - struct S { b: bool } + struct S { b: bool } foo() { let x: Self.S; diff --git a/language/ir-testsuite/tests/generics/signatures/resource_as_unrestricted.mvir b/language/ir-testsuite/tests/generics/signatures/resource_as_unrestricted.mvir index 9c3a0f1de941..3e5f63f2c0ad 100644 --- a/language/ir-testsuite/tests/generics/signatures/resource_as_unrestricted.mvir +++ b/language/ir-testsuite/tests/generics/signatures/resource_as_unrestricted.mvir @@ -1,5 +1,5 @@ module M { - struct S { b: bool } + struct S { b: bool } resource R { b: bool } foo() { diff --git a/language/ir-testsuite/tests/generics/signatures/too_few_type_actuals.mvir b/language/ir-testsuite/tests/generics/signatures/too_few_type_actuals.mvir index ffb9a4573f07..d69da18869da 100644 --- a/language/ir-testsuite/tests/generics/signatures/too_few_type_actuals.mvir +++ b/language/ir-testsuite/tests/generics/signatures/too_few_type_actuals.mvir @@ -7,4 +7,4 @@ module M { } } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/generics/signatures/too_many_type_actuals.mvir b/language/ir-testsuite/tests/generics/signatures/too_many_type_actuals.mvir index e3533d0366e4..a428a5c0d999 100644 --- a/language/ir-testsuite/tests/generics/signatures/too_many_type_actuals.mvir +++ b/language/ir-testsuite/tests/generics/signatures/too_many_type_actuals.mvir @@ -7,4 +7,4 @@ module M { } } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/generics/signatures/two_type_actuals_ok.mvir b/language/ir-testsuite/tests/generics/signatures/two_type_actuals_ok.mvir index 9c91a55861f7..74b5e51c1093 100644 --- a/language/ir-testsuite/tests/generics/signatures/two_type_actuals_ok.mvir +++ b/language/ir-testsuite/tests/generics/signatures/two_type_actuals_ok.mvir @@ -1,6 +1,6 @@ module M { resource R { b: bool} - struct S { b: bool } + struct S { b: bool } foo() { let x: Self.S; diff --git a/language/ir-testsuite/tests/generics/signatures/two_type_actuals_reverse_order.mvir b/language/ir-testsuite/tests/generics/signatures/two_type_actuals_reverse_order.mvir index 4bfc5205f8c8..43417a950e72 100644 --- a/language/ir-testsuite/tests/generics/signatures/two_type_actuals_reverse_order.mvir +++ b/language/ir-testsuite/tests/generics/signatures/two_type_actuals_reverse_order.mvir @@ -1,6 +1,6 @@ module M { resource R { b: bool } - struct S { b: bool } + struct S { b: bool } foo() { let x: Self.S; diff --git a/language/ir-testsuite/tests/generics/struct_def.mvir b/language/ir-testsuite/tests/generics/struct_def.mvir index cdfc4362867e..ad8a82819ec0 100644 --- a/language/ir-testsuite/tests/generics/struct_def.mvir +++ b/language/ir-testsuite/tests/generics/struct_def.mvir @@ -4,5 +4,5 @@ module M { struct Foo { x: T } - struct Bar { x1: T2, x2: T3, x3: T4, x4: T1 } + struct Bar { x1: T2, x2: T3, x3: T4, x4: T1 } } diff --git a/language/ir-testsuite/tests/generics/unpack.mvir b/language/ir-testsuite/tests/generics/unpack.mvir index 7b09cc4dbc52..e4caa940ce4c 100644 --- a/language/ir-testsuite/tests/generics/unpack.mvir +++ b/language/ir-testsuite/tests/generics/unpack.mvir @@ -11,7 +11,7 @@ module M { //! new-transaction module N { - resource Foo { x: T1, y: T2 } + resource Foo { x: T1, y: T2 } foo() { let x: u64; diff --git a/language/ir-testsuite/tests/natives/event_emit_struct_with_type_params.mvir b/language/ir-testsuite/tests/natives/event_emit_struct_with_type_params.mvir index c535f68792a9..cd34cb3bfff5 100644 --- a/language/ir-testsuite/tests/natives/event_emit_struct_with_type_params.mvir +++ b/language/ir-testsuite/tests/natives/event_emit_struct_with_type_params.mvir @@ -3,7 +3,7 @@ module M { struct MyEvent { b: bool } - public emit_event() { + public emit_event() { let handle: LibraAccount.EventHandle>; handle = LibraAccount.new_event_handle>(); LibraAccount.emit_event>(&mut handle, MyEvent{ b: true }); diff --git a/language/ir-testsuite/tests/scripts/script_too_few_type_args.mvir b/language/ir-testsuite/tests/scripts/script_too_few_type_args.mvir index 02eb59e6c21d..3bc6892151a2 100644 --- a/language/ir-testsuite/tests/scripts/script_too_few_type_args.mvir +++ b/language/ir-testsuite/tests/scripts/script_too_few_type_args.mvir @@ -1,4 +1,4 @@ main() { return; } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/scripts/script_too_few_type_args_inner.mvir b/language/ir-testsuite/tests/scripts/script_too_few_type_args_inner.mvir index d4435be15a86..6d98c9516c26 100644 --- a/language/ir-testsuite/tests/scripts/script_too_few_type_args_inner.mvir +++ b/language/ir-testsuite/tests/scripts/script_too_few_type_args_inner.mvir @@ -2,4 +2,4 @@ main() { return; } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/scripts/script_too_many_type_args.mvir b/language/ir-testsuite/tests/scripts/script_too_many_type_args.mvir index 960a5948db63..1a4322739c63 100644 --- a/language/ir-testsuite/tests/scripts/script_too_many_type_args.mvir +++ b/language/ir-testsuite/tests/scripts/script_too_many_type_args.mvir @@ -2,4 +2,4 @@ main() { return; } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/scripts/script_too_many_type_args_inner.mvir b/language/ir-testsuite/tests/scripts/script_too_many_type_args_inner.mvir index 554ffed738be..31f6f500f9ef 100644 --- a/language/ir-testsuite/tests/scripts/script_too_many_type_args_inner.mvir +++ b/language/ir-testsuite/tests/scripts/script_too_many_type_args_inner.mvir @@ -2,4 +2,4 @@ main() { return; } -// check: NUMBER_OF_TYPE_ACTUALS_MISMATCH +// check: NUMBER_OF_TYPE_ARGUMENTS_MISMATCH diff --git a/language/ir-testsuite/tests/scripts/script_type_arg_kind_mismatch_1.mvir b/language/ir-testsuite/tests/scripts/script_type_arg_kind_mismatch_1.mvir index 1164be67d74d..941c60dfbfe4 100644 --- a/language/ir-testsuite/tests/scripts/script_type_arg_kind_mismatch_1.mvir +++ b/language/ir-testsuite/tests/scripts/script_type_arg_kind_mismatch_1.mvir @@ -1,5 +1,5 @@ //! type-args: 0x0::LibraAccount::T -main() { +main() { return; } // check: CONTRAINT_KIND_MISMATCH diff --git a/language/ir-testsuite/tests/scripts/script_type_parameters_in_args.mvir b/language/ir-testsuite/tests/scripts/script_type_parameters_in_args.mvir index 917222488ab7..3a2eaba9a577 100644 --- a/language/ir-testsuite/tests/scripts/script_type_parameters_in_args.mvir +++ b/language/ir-testsuite/tests/scripts/script_type_parameters_in_args.mvir @@ -1,4 +1,4 @@ -main(x: T) { +main(x: T) { return; } // check: INVALID_MAIN_FUNCTION_SIGNATURE @@ -12,7 +12,7 @@ main(x: &T) { //! new-transaction -main(v: vector) { +main(v: vector) { return; } // check: INVALID_MAIN_FUNCTION_SIGNATURE @@ -28,7 +28,7 @@ module M { //! new-transaction import {{default}}.M; -main(x: M.Box>) { +main(x: M.Box>) { return; } // check: INVALID_MAIN_FUNCTION_SIGNATURE diff --git a/language/ir-testsuite/tests/scripts/script_with_type_parameters.mvir b/language/ir-testsuite/tests/scripts/script_with_type_parameters.mvir index 10dc27580023..240b1ff3a9f0 100644 --- a/language/ir-testsuite/tests/scripts/script_with_type_parameters.mvir +++ b/language/ir-testsuite/tests/scripts/script_with_type_parameters.mvir @@ -1,5 +1,5 @@ //! type-args: u64, u8, 0x0::LibraAccount::T -main() { +main() { return; } // check: EXECUTED diff --git a/language/move-core/types/src/identifier.rs b/language/move-core/types/src/identifier.rs index f341fdec5a07..f121d24f0482 100644 --- a/language/move-core/types/src/identifier.rs +++ b/language/move-core/types/src/identifier.rs @@ -57,8 +57,12 @@ fn is_valid(s: &str) -> bool { /// A regex describing what identifiers are allowed. Used for proptests. // TODO: "" is coded as an exception. It should be removed once CompiledScript goes away. #[cfg(any(test, feature = "fuzzing"))] +#[allow(dead_code)] pub(crate) static ALLOWED_IDENTIFIERS: &str = r"(?:[a-zA-Z][a-zA-Z0-9_]*)|(?:_[a-zA-Z0-9_]+)|(?:)"; +#[cfg(any(test, feature = "fuzzing"))] +pub(crate) static ALLOWED_NO_SELF_IDENTIFIERS: &str = + r"(?:[a-zA-Z][a-zA-Z0-9_]*)|(?:_[a-zA-Z0-9_]+)"; /// An owned identifier. /// @@ -207,7 +211,7 @@ impl Arbitrary for Identifier { type Strategy = BoxedStrategy; fn arbitrary_with((): ()) -> Self::Strategy { - ALLOWED_IDENTIFIERS + ALLOWED_NO_SELF_IDENTIFIERS .prop_map(|s| { // Identifier::new will verify that generated identifiers are correct. Identifier::new(s).unwrap() diff --git a/language/move-ir/types/src/ast.rs b/language/move-ir/types/src/ast.rs index 7d71742aed83..f9d348427e4b 100644 --- a/language/move-ir/types/src/ast.rs +++ b/language/move-ir/types/src/ast.rs @@ -158,8 +158,8 @@ pub enum Kind { All, /// `Resource` types must follow move semantics and various resource safety rules. Resource, - /// `Unrestricted` types do not need to follow the `Resource` rules. - Unrestricted, + /// `Copyable` types do not need to follow the `Resource` rules. + Copyable, } //************************************************************************************************** @@ -953,12 +953,12 @@ impl FunctionSignature { pub fn new( formals: Vec<(Var, Type)>, return_type: Vec, - type_formals: Vec<(TypeVar, Kind)>, + type_parameters: Vec<(TypeVar, Kind)>, ) -> Self { FunctionSignature { formals, return_type, - type_formals, + type_formals: type_parameters, } } } @@ -970,12 +970,12 @@ impl Function_ { visibility: FunctionVisibility, formals: Vec<(Var, Type)>, return_type: Vec, - type_formals: Vec<(TypeVar, Kind)>, + type_parameters: Vec<(TypeVar, Kind)>, acquires: Vec, specifications: Vec, body: FunctionBody, ) -> Self { - let signature = FunctionSignature::new(formals, return_type, type_formals); + let signature = FunctionSignature::new(formals, return_type, type_parameters); Function_ { visibility, signature, @@ -1231,7 +1231,7 @@ impl fmt::Display for Kind { match self { Kind::All => "all", Kind::Resource => "resource", - Kind::Unrestricted => "unrestricted", + Kind::Copyable => "copyable", } ) } diff --git a/language/move-lang/src/compiled_unit.rs b/language/move-lang/src/compiled_unit.rs index 494879e4c260..927d13f52601 100644 --- a/language/move-lang/src/compiled_unit.rs +++ b/language/move-lang/src/compiled_unit.rs @@ -60,8 +60,8 @@ impl CompiledUnit { #[allow(dead_code)] pub fn serialize_debug(self) -> Vec { match self { - CompiledUnit::Module { module, .. } => format!("{}", module), - CompiledUnit::Script { script, .. } => format!("{}", script), + CompiledUnit::Module { module, .. } => format!("{:?}", module), + CompiledUnit::Script { script, .. } => format!("{:?}", script), } .into() } diff --git a/language/move-lang/src/hlir/ast.rs b/language/move-lang/src/hlir/ast.rs index 51d1adff2f2f..3cf11da8cf9e 100644 --- a/language/move-lang/src/hlir/ast.rs +++ b/language/move-lang/src/hlir/ast.rs @@ -342,7 +342,7 @@ impl BaseType_ { use BuiltinTypeName_::*; let kind = match b_ { - U8 | U64 | U128 | Bool | Address => sp(loc, Kind_::Unrestricted), + U8 | U64 | U128 | Bool | Address => sp(loc, Kind_::Copyable), Vector => { assert!( ty_args.len() == 1, @@ -398,7 +398,7 @@ impl SingleType_ { pub fn kind(&self, loc: Loc) -> Kind { match self { - SingleType_::Ref(_, _) => sp(loc, Kind_::Unrestricted), + SingleType_::Ref(_, _) => sp(loc, Kind_::Copyable), SingleType_::Base(b) => b.value.kind(), } } diff --git a/language/move-lang/src/ir_translation.rs b/language/move-lang/src/ir_translation.rs index 1ecdd2123169..43d0c7b9e985 100644 --- a/language/move-lang/src/ir_translation.rs +++ b/language/move-lang/src/ir_translation.rs @@ -28,8 +28,8 @@ pub fn fix_syntax_and_write(out_path: &Path, contents: String) { let contents = replace!(contents, r"copy\((\w+)\)", "copy $1"); // resource StructName ~> resource struct StructName let contents = replace!(contents, r"resource\s+(\w)", "resource struct $1"); - // unrestricted ~> copyable - let contents = replace!(contents, r":\s*unrestricted", NoExpand(": copyable")); + // copyable ~> copyable + let contents = replace!(contents, r":\s*copyable", NoExpand(": copyable")); // import ~> use let contents = replace!(contents, r"import", NoExpand("use")); // Self. is unnecessary diff --git a/language/move-lang/src/naming/ast.rs b/language/move-lang/src/naming/ast.rs index a5b00440a28c..6f35f635f076 100644 --- a/language/move-lang/src/naming/ast.rs +++ b/language/move-lang/src/naming/ast.rs @@ -357,7 +357,7 @@ impl Type_ { use BuiltinTypeName_::*; let kind = match b.value { - U8 | U64 | U128 | Address | Bool => Some(sp(b.loc, Kind_::Unrestricted)), + U8 | U64 | U128 | Address | Bool => Some(sp(b.loc, Kind_::Copyable)), Vector => None, }; let n = sp(b.loc, TypeName_::Builtin(b)); @@ -621,7 +621,7 @@ impl AstDebug for TParam { Kind_::Unknown => (), Kind_::Resource => w.write(": resource"), Kind_::Affine => w.write(": copyable"), - Kind_::Unrestricted => panic!("ICE 'unrestricted' kind constraint"), + Kind_::Copyable => panic!("ICE 'copyable' kind constraint"), } } } diff --git a/language/move-lang/src/parser/ast.rs b/language/move-lang/src/parser/ast.rs index 0c2d373ba9d9..5e820fb95936 100644 --- a/language/move-lang/src/parser/ast.rs +++ b/language/move-lang/src/parser/ast.rs @@ -254,7 +254,7 @@ pub enum Kind_ { // Explicitly copyable types Affine, // Implicitly copyable types - Unrestricted, + Copyable, } pub type Kind = Spanned; @@ -520,7 +520,7 @@ impl Kind_ { pub fn is_resourceful(&self) -> bool { match self { - Kind_::Affine | Kind_::Unrestricted => false, + Kind_::Affine | Kind_::Copyable => false, Kind_::Resource | Kind_::Unknown => true, } } @@ -919,7 +919,7 @@ impl AstDebug for (Name, Kind) { w.write(": "); k.ast_debug(w) } - Kind_::Unrestricted => panic!("ICE 'unrestricted' kind constraint"), + Kind_::Copyable => panic!("ICE 'copyable' kind constraint"), } } } @@ -930,7 +930,7 @@ impl AstDebug for Kind_ { Kind_::Unknown => "unknown", Kind_::Resource => "resource", Kind_::Affine => "copyable", - Kind_::Unrestricted => "unrestricted", + Kind_::Copyable => "copyable", }) } } diff --git a/language/move-lang/src/to_bytecode/translate.rs b/language/move-lang/src/to_bytecode/translate.rs index 8aa0f4902d51..ae7cf560b632 100644 --- a/language/move-lang/src/to_bytecode/translate.rs +++ b/language/move-lang/src/to_bytecode/translate.rs @@ -369,11 +369,11 @@ fn function_signature(context: &mut Context, sig: H::FunctionSignature) -> IR::F .into_iter() .map(|(v, st)| (var(v), single_type(context, st))) .collect(); - let type_formals = type_parameters(sig.type_parameters); + let type_parameters = type_parameters(sig.type_parameters); IR::FunctionSignature { return_type, formals, - type_formals, + type_formals: type_parameters, } } @@ -518,7 +518,7 @@ fn kind(sp!(_, k_): &Kind) -> IR::Kind { match k_ { GK::Unknown => IRK::All, GK::Resource => IRK::Resource, - GK::Affine | GK::Unrestricted => IRK::Unrestricted, + GK::Affine | GK::Copyable => IRK::Copyable, } } diff --git a/language/move-lang/src/typing/core.rs b/language/move-lang/src/typing/core.rs index 05f0388d3c70..519ebf12857a 100644 --- a/language/move-lang/src/typing/core.rs +++ b/language/move-lang/src/typing/core.rs @@ -395,7 +395,7 @@ pub fn infer_kind(context: &Context, subst: &Subst, ty: Type) -> Option { use Type_ as T; let loc = ty.loc; match unfold_type(subst, ty).value { - T::Unit | T::Ref(_, _) => Some(sp(loc, Kind_::Unrestricted)), + T::Unit | T::Ref(_, _) => Some(sp(loc, Kind_::Copyable)), T::Var(_) => panic!("ICE unfold_type failed, which is impossible"), T::UnresolvedError | T::Anything => None, T::Param(TParam { kind, .. }) | T::Apply(Some(kind), _, _) => Some(kind), @@ -422,12 +422,12 @@ pub fn infer_kind(context: &Context, subst: &Subst, ty: Type) -> Option { .zip(contraints) .filter_map(|(t, constraint_opt)| infer_kind(context, subst, t).or(constraint_opt)) .map(|k| match k { - sp!(loc, K::Unrestricted) => sp(loc, K::Affine), + sp!(loc, K::Copyable) => sp(loc, K::Affine), k => k, }) .max_by(most_general_kind); Some(match max { - Some(sp!(_, K::Unrestricted)) => unreachable!(), + Some(sp!(_, K::Copyable)) => unreachable!(), None | Some(sp!(_, K::Affine)) => { sp(type_name_declared_loc(context, &n), K::Affine) } @@ -441,7 +441,7 @@ fn most_general_kind(k1: &Kind, k2: &Kind) -> std::cmp::Ordering { use std::cmp::Ordering as O; use Kind_ as K; match (&k1.value, &k2.value) { - (K::Unrestricted, _) | (_, K::Unrestricted) => panic!("ICE structs cannot be unrestricted"), + (K::Copyable, _) | (_, K::Copyable) => panic!("ICE structs cannot be copyable"), (K::Unknown, K::Unknown) => O::Equal, (K::Unknown, _) => O::Greater, @@ -717,21 +717,21 @@ fn solve_kind_constraint(context: &mut Context, loc: Loc, b: Type, k: Kind) { Some(k) => k, }; match (b_kind.value, &k.value) { - (_, K::Unrestricted) => panic!("ICE tparams cannot have unrestricted constraints"), + (_, K::Copyable) => panic!("ICE tparams cannot have copyable constraints"), // _ <: all - // unrestricted <: affine + // copyable <: affine // affine <: affine // linear <: linear (_, K::Unknown) - | (K::Unrestricted, K::Affine) + | (K::Copyable, K::Affine) | (K::Affine, K::Affine) | (K::Resource, K::Resource) => (), - // unrestricted { + (K::Copyable, K::Resource) | (K::Affine, K::Resource) | (K::Unknown, K::Resource) => { let ty_str = error_format(&b, &context.subst); let cmsg = format!( "The {} type {} does not satisfy the constraint '{}'", @@ -757,7 +757,7 @@ fn solve_kind_constraint(context: &mut Context, loc: Loc, b: Type, k: Kind) { // linear { let resource_msg = match bk { - K::Unrestricted | K::Affine => panic!("ICE covered above"), + K::Copyable | K::Affine => panic!("ICE covered above"), K::Resource => "resource ", K::Unknown => "", }; @@ -794,7 +794,7 @@ fn solve_copyable_constraint(context: &mut Context, loc: Loc, msg: String, s: Ty Some(k) => k, }; match kind { - sp!(_, Kind_::Unrestricted) | sp!(_, Kind_::Affine) => (), + sp!(_, Kind_::Copyable) | sp!(_, Kind_::Affine) => (), sp!(rloc, Kind_::Unknown) | sp!(rloc, Kind_::Resource) => { let ty_str = error_format(&s, &context.subst); context.error(vec![ @@ -822,7 +822,7 @@ fn solve_implicitly_copyable_constraint( Some(k) => k, }; match kind { - sp!(_, Kind_::Unrestricted) => (), + sp!(_, Kind_::Copyable) => (), sp!(kloc, Kind_::Affine) => { let ty_str = error_format(&ty, &context.subst); context.error(vec![ diff --git a/language/move-lang/src/typing/expand.rs b/language/move-lang/src/typing/expand.rs index e51ac26556c9..759d03c449b8 100644 --- a/language/move-lang/src/typing/expand.rs +++ b/language/move-lang/src/typing/expand.rs @@ -85,7 +85,7 @@ pub fn type_(context: &mut Context, ty: &mut Type) { fn get_kind(sp!(loc, ty_): &Type) -> Kind { use Type_::*; match ty_ { - Anything | UnresolvedError | Unit | Ref(_, _) => sp(*loc, Kind_::Unrestricted), + Anything | UnresolvedError | Unit | Ref(_, _) => sp(*loc, Kind_::Copyable), Var(_) => panic!("ICE unexpanded type"), Param(TParam { kind, .. }) => kind.clone(), Apply(Some(kind), _, _) => kind.clone(), @@ -153,7 +153,7 @@ fn exp(context: &mut Context, e: &mut T::Exp) { let from_user = false; let var = v.clone(); e.exp.value = match get_kind(&e.ty).value { - Kind_::Unrestricted => E::Copy { from_user, var }, + Kind_::Copyable => E::Copy { from_user, var }, Kind_::Unknown | Kind_::Affine | Kind_::Resource => E::Move { from_user, var }, } } diff --git a/language/move-prover/bytecode-to-boogie/test_mvir/test-generics.mvir b/language/move-prover/bytecode-to-boogie/test_mvir/test-generics.mvir index cc9eb28750c3..7fc685eb6a91 100644 --- a/language/move-prover/bytecode-to-boogie/test_mvir/test-generics.mvir +++ b/language/move-prover/bytecode-to-boogie/test_mvir/test-generics.mvir @@ -5,7 +5,7 @@ module TestGenerics { v: vector } - struct T { + struct T { v: vector } @@ -20,14 +20,14 @@ module TestGenerics { return; } - public create(x: E): Self.T { + public create(x: E): Self.T { let v: vector; v = Vector.empty(); Vector.push_back(&mut v, move(x)); return T{ v: move(v) }; } - public overcomplicated_equals(x: E, y: E): bool { + public overcomplicated_equals(x: E, y: E): bool { let r: bool; let x1: Self.T; let y1: Self.T; diff --git a/language/move-prover/bytecode-to-boogie/test_mvir/vector.mvir b/language/move-prover/bytecode-to-boogie/test_mvir/vector.mvir index 521f5895453b..0e27acd81a25 100644 --- a/language/move-prover/bytecode-to-boogie/test_mvir/vector.mvir +++ b/language/move-prover/bytecode-to-boogie/test_mvir/vector.mvir @@ -16,7 +16,7 @@ module Option { return T { v: move(v) }; } - public unwrap_or(x: Self.T, e: E): E { + public unwrap_or(x: Self.T, e: E): E { let v: vector; T { v: v } = move(x); if (Vector.is_empty(&v)) { diff --git a/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/libra_account.mvir b/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/libra_account.mvir index a1988390fdaa..727e01c312c0 100644 --- a/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/libra_account.mvir +++ b/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/libra_account.mvir @@ -73,7 +73,7 @@ module LibraAccount { // A handle for a event such that: // 1. Other modules can emit event to this handle. // 2. Storage can use this handle to prove the total number of events that happened in the past. - resource EventHandle { + resource EventHandle { // Total number of events emitted to this event stream. counter: u64, // A globally unique ID for this event stream. @@ -743,14 +743,14 @@ module LibraAccount { } // Use EventHandleGenerator to generate a unique event handle that one can emit an event to. - new_event_handle_impl(counter: &mut Self.EventHandleGenerator, sender: address): Self.EventHandle + new_event_handle_impl(counter: &mut Self.EventHandleGenerator, sender: address): Self.EventHandle aborts_if counter.counter + 1 > max_u64() { return EventHandle {counter: 0, guid: Self.fresh_guid(move(counter), move(sender))}; } // Use sender's EventHandleGenerator to generate a unique event handle that one can emit an event to. - public new_event_handle(): Self.EventHandle acquires T + public new_event_handle(): Self.EventHandle acquires T aborts_if global(txn_sender).event_generator.counter + 1 > max_u64() { let sender_account_ref: &mut Self.T; @@ -761,7 +761,7 @@ module LibraAccount { // Emit an event with payload `msg` by using handle's key and counter. Will change the payload from vector to a // generic type parameter once we have generics. - public emit_event(handle_ref: &mut Self.EventHandle, msg: T) + public emit_event(handle_ref: &mut Self.EventHandle, msg: T) aborts_if handle_ref.counter + 1 > max_u64() { let count: &mut u64; @@ -777,11 +777,11 @@ module LibraAccount { // Native procedure that writes to the actual event stream in Event store // This will replace the "native" portion of EmitEvent bytecode - native write_to_event_store(guid: vector, count: u64, msg: T) + native write_to_event_store(guid: vector, count: u64, msg: T) aborts_if false; // Destroy a unique handle. - public destroy_handle(handle: Self.EventHandle) + public destroy_handle(handle: Self.EventHandle) aborts_if false { let guid: vector; diff --git a/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/vector.mvir b/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/vector.mvir index 64c56e9c516a..d13f444e20cf 100644 --- a/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/vector.mvir +++ b/language/move-prover/bytecode-to-boogie/test_mvir/verify-stdlib/vector.mvir @@ -1,4 +1,4 @@ -// A variable-sized container that can hold both unrestricted types and resources. +// A variable-sized container that can hold both copyable types and resources. // Except for this comment, this file is just a copy of language/stdlib/modules/vector.mvir // This hasn't been specified, yet. This is only needed for MVIR type checking, since the definitions @@ -76,15 +76,15 @@ module Vector { return Self.length(move(v)) == 0; } - // Return the `i`th element of the vector. Only available for vectors that contain unrestricted + // Return the `i`th element of the vector. Only available for vectors that contain copyable // values. - public get(v: &vector, i: u64): Element { + public get(v: &vector, i: u64): Element { return *Self.borrow(move(v), move(i)); } // Replace the `i`th element of the vector with the value `v`. Only available for vectors that - // contain unrestricted values. - public set(v: &mut vector, i: u64, e: Element) { + // contain copyable values. + public set(v: &mut vector, i: u64, e: Element) { *(Self.borrow_mut(move(v), move(i))) = move(e); return; } diff --git a/language/move-prover/bytecode-to-boogie/test_mvir/verify-vector.mvir b/language/move-prover/bytecode-to-boogie/test_mvir/verify-vector.mvir index 95f0e86a4aba..bd13344a9e36 100644 --- a/language/move-prover/bytecode-to-boogie/test_mvir/verify-vector.mvir +++ b/language/move-prover/bytecode-to-boogie/test_mvir/verify-vector.mvir @@ -358,7 +358,7 @@ module VerifyVector { } // succeeds. standard vector method. - public my_get4(v: &vector, i: u64): Element + public my_get4(v: &vector, i: u64): Element aborts_if i >= len(*v) //ensures RET(0) == vector_get(*v, i) ensures RET(0) == v[i] @@ -385,7 +385,7 @@ module VerifyVector { } // succeeds. standard vector method. - public my_set3(v: &mut vector, i: u64, e: Element) + public my_set3(v: &mut vector, i: u64, e: Element) aborts_if i >= len(*v) ensures *v == old(v[i:=e]) //ensures *v == old(*v)[i:=e] // mvir parsing errors: ParserError: Invalid token at 11189 @@ -512,7 +512,7 @@ module VerifyVector { } // succeeds. custom vector method. - public my_swap3(v: &mut vector, i: u64, j: u64) + public my_swap3(v: &mut vector, i: u64, j: u64) aborts_if i >= len(*v) aborts_if j >= len(*v) ensures *v == old(v[i:=v[j]][j:=v[i]]) diff --git a/language/move-prover/scripts/install-dotnet.sh b/language/move-prover/scripts/install-dotnet.sh old mode 100644 new mode 100755 diff --git a/language/move-prover/spec-lang/src/env.rs b/language/move-prover/spec-lang/src/env.rs index 002d65b2cd47..a7a004a77d0e 100644 --- a/language/move-prover/spec-lang/src/env.rs +++ b/language/move-prover/spec-lang/src/env.rs @@ -22,13 +22,13 @@ use libra_types::language_storage; use vm::{ access::ModuleAccess, file_format::{ - AddressPoolIndex, CodeOffset, FieldDefinitionIndex, FunctionDefinitionIndex, - FunctionHandleIndex, Kind, LocalsSignatureIndex, SignatureToken, StructDefinitionIndex, - StructFieldInformation, StructHandleIndex, + AddressPoolIndex, CodeOffset, FunctionDefinitionIndex, FunctionHandleIndex, Kind, + SignatureIndex, SignatureToken, StructDefinitionIndex, StructFieldInformation, + StructHandleIndex, }, views::{ - FieldDefinitionView, FunctionDefinitionView, FunctionHandleView, SignatureTokenView, - StructDefinitionView, StructHandleView, ViewInternals, + FunctionDefinitionView, FunctionHandleView, SignatureTokenView, StructDefinitionView, + StructHandleView, }, }; @@ -468,28 +468,27 @@ impl GlobalEnv { invariants: Vec, ) -> StructData { let handle_idx = module.struct_def_at(def_idx).struct_handle; - let field_data = if let StructFieldInformation::Declared { - field_count, - fields, - } = module.struct_def_at(def_idx).field_information + let field_data = if let StructFieldInformation::Declared(fields) = + &module.struct_def_at(def_idx).field_information { let mut map = BTreeMap::new(); - for idx in fields.0..fields.0 + field_count { - let def_idx = FieldDefinitionIndex(idx); - let def = module.field_def_at(def_idx); + for (offset, field) in fields.iter().enumerate() { let name = self .symbol_pool - .make(module.identifier_at(def.name).as_str()); - map.insert(FieldId(name), FieldData { name, def_idx }); + .make(module.identifier_at(field.name).as_str()); + map.insert( + FieldId(name), + FieldData { + name, + def_idx, + offset, + }, + ); } map } else { BTreeMap::new() }; - let field_def_idx_to_id: BTreeMap = field_data - .iter() - .map(|(id, data)| (data.def_idx, *id)) - .collect(); let mut data_invariants = vec![]; let mut update_invariants = vec![]; let mut pack_invariants = vec![]; @@ -510,7 +509,6 @@ impl GlobalEnv { def_idx, handle_idx, field_data, - field_def_idx_to_id, data_invariants, update_invariants, pack_invariants, @@ -841,18 +839,6 @@ impl<'env> ModuleEnv<'env> { self.data.struct_data.len() } - /// Gets the struct declaring a field specified by FieldDefinitionIndex, - /// as it is globally unique for this module. - pub fn get_struct_of_field(&self, idx: &FieldDefinitionIndex) -> StructEnv<'_> { - let field_view = - FieldDefinitionView::new(&self.data.module, self.data.module.field_def_at(*idx)); - let struct_name = self - .env - .symbol_pool - .make(field_view.member_of().name().as_str()); - self.find_struct(struct_name).expect("struct undefined") - } - /// Returns iterator over structs in this module. pub fn get_structs(&'env self) -> impl Iterator> { self.clone().into_structs() @@ -885,7 +871,21 @@ impl<'env> ModuleEnv<'env> { } SignatureToken::TypeParameter(index) => Type::TypeParameter(*index), SignatureToken::Vector(bt) => Type::Vector(Box::new(self.globalize_signature(&*bt))), - SignatureToken::Struct(handle_idx, args) => { + SignatureToken::Struct(handle_idx) => { + let struct_view = StructHandleView::new( + &self.data.module, + self.data.module.struct_handle_at(*handle_idx), + ); + let declaring_module_env = self + .env + .find_module(&self.env.to_module_name(&struct_view.module_id())) + .expect("undefined module"); + let struct_env = declaring_module_env + .find_struct(self.env.symbol_pool.make(struct_view.name().as_str())) + .expect("undefined struct"); + Type::Struct(declaring_module_env.data.id, struct_env.get_id(), vec![]) + } + SignatureToken::StructInstantiation(handle_idx, args) => { let struct_view = StructHandleView::new( &self.data.module, self.data.module.struct_handle_at(*handle_idx), @@ -914,9 +914,14 @@ impl<'env> ModuleEnv<'env> { } /// Gets a list of type actuals associated with the index in the bytecode. - pub fn get_type_actuals(&self, idx: LocalsSignatureIndex) -> Vec { - let actuals = &self.data.module.locals_signature_at(idx).0; - self.globalize_signatures(actuals) + pub fn get_type_actuals(&self, idx: Option) -> Vec { + match idx { + Some(idx) => { + let actuals = &self.data.module.signature_at(idx).0; + self.globalize_signatures(actuals) + } + None => vec![], + } } /// Converts an address pool index for this module into a number representing the address. @@ -1009,9 +1014,6 @@ pub struct StructData { /// Field definitions. field_data: BTreeMap, - /// Map from definition index to id. - field_def_idx_to_id: BTreeMap, - // Invariants data_invariants: Vec, update_invariants: Vec, @@ -1079,13 +1081,10 @@ impl<'env> StructEnv<'env> { /// Get an iterator for the fields. pub fn get_fields(&'env self) -> impl Iterator> { - self.data - .field_def_idx_to_id - .values() - .map(move |field_id| FieldEnv { - struct_env: self.clone(), - data: &self.data.field_data[field_id], - }) + self.data.field_data.values().map(move |data| FieldEnv { + struct_env: self.clone(), + data, + }) } /// Gets a field by its id. @@ -1097,17 +1096,6 @@ impl<'env> StructEnv<'env> { } } - /// Gets a field by its definition index. - pub fn get_field_by_def_idx(&'env self, idx: FieldDefinitionIndex) -> FieldEnv<'env> { - self.get_field( - *self - .data - .field_def_idx_to_id - .get(&idx) - .expect("undefined field definition index"), - ) - } - /// Find a field by its name. pub fn find_field(&'env self, name: Symbol) -> Option> { let id = FieldId(name); @@ -1117,6 +1105,19 @@ impl<'env> StructEnv<'env> { }) } + /// Gets a field by its id. + pub fn get_field_by_offset(&'env self, offset: usize) -> FieldEnv<'env> { + for data in self.data.field_data.values() { + if data.offset == offset { + return FieldEnv { + struct_env: self.clone(), + data, + }; + } + } + unreachable!("invalid field lookup") + } + /// Returns the type parameters associated with this struct. pub fn get_type_parameters(&self) -> Vec { // TODO: we currently do not know the original names of those formals, so we generate them. @@ -1124,7 +1125,7 @@ impl<'env> StructEnv<'env> { &self.module_env.data.module, self.module_env.data.module.struct_def_at(self.data.def_idx), ); - view.type_formals() + view.type_parameters() .iter() .enumerate() .map(|(i, k)| { @@ -1172,8 +1173,9 @@ pub struct FieldData { /// The name of this field. name: Symbol, - /// The definition index of this field in its module. - def_idx: FieldDefinitionIndex, + /// The struct definition index of this field in its module. + def_idx: StructDefinitionIndex, + offset: usize, } #[derive(Debug)] @@ -1198,17 +1200,19 @@ impl<'env> FieldEnv<'env> { /// Gets the type of this field. pub fn get_type(&self) -> Type { - let view = FieldDefinitionView::new( - &self.struct_env.module_env.data.module, - self.struct_env - .module_env - .data - .module - .field_def_at(self.data.def_idx), - ); + let struct_def = self + .struct_env + .module_env + .data + .module + .struct_def_at(self.data.def_idx); + let field = match &struct_def.field_information { + StructFieldInformation::Declared(fields) => &fields[self.data.offset], + StructFieldInformation::Native => unreachable!(), + }; self.struct_env .module_env - .globalize_signature(view.type_signature().token().as_inner()) + .globalize_signature(&field.signature.0) } } @@ -1321,8 +1325,7 @@ impl<'env> FunctionEnv<'env> { // TODO: currently the translation scheme isn't working with using real type // parameter names, so use indices instead. let view = self.definition_view(); - view.signature() - .type_formals() + view.type_parameters() .iter() .enumerate() .map(|(i, k)| { @@ -1337,8 +1340,7 @@ impl<'env> FunctionEnv<'env> { /// Returns the regular parameters associated with this function. pub fn get_parameters(&self) -> Vec { let view = self.definition_view(); - view.signature() - .arg_tokens() + view.arg_tokens() .map(|tv: SignatureTokenView| { self.module_env.globalize_signature(tv.signature_token()) }) @@ -1350,8 +1352,7 @@ impl<'env> FunctionEnv<'env> { /// Returns return types of this function. pub fn get_return_types(&self) -> Vec { let view = self.definition_view(); - view.signature() - .return_tokens() + view.return_tokens() .map(|tv: SignatureTokenView| { self.module_env.globalize_signature(tv.signature_token()) }) @@ -1361,7 +1362,7 @@ impl<'env> FunctionEnv<'env> { /// Returns the number of return values of this function. pub fn get_return_count(&self) -> usize { let view = self.definition_view(); - view.signature().return_count() + view.return_count() } /// Get the name to be used for a local. If the local is an argument, use that for naming, diff --git a/language/move-prover/src/bytecode_translator.rs b/language/move-prover/src/bytecode_translator.rs index bd231bd91c82..567f013ca161 100644 --- a/language/move-prover/src/bytecode_translator.rs +++ b/language/move-prover/src/bytecode_translator.rs @@ -1035,9 +1035,9 @@ impl<'env> ModuleTranslator<'env> { emitln!(self.writer, s); } } - BorrowField(dest, src, field_def_index) => { - let struct_env = self.module_env.get_struct_of_field(field_def_index); - let field_env = &struct_env.get_field_by_def_idx(*field_def_index); + BorrowField(dest, src, struct_def_index, field_offset) => { + let struct_env = self.module_env.get_struct_by_def_idx(*struct_def_index); + let field_env = &struct_env.get_field_by_offset(*field_offset); emitln!( self.writer, "call {} := BorrowField({}, {});", diff --git a/language/move-prover/stackless-bytecode-generator/src/lifetime_analysis.rs b/language/move-prover/stackless-bytecode-generator/src/lifetime_analysis.rs index ea7b2c4468bc..d1311ca5b1a2 100644 --- a/language/move-prover/stackless-bytecode-generator/src/lifetime_analysis.rs +++ b/language/move-prover/stackless-bytecode-generator/src/lifetime_analysis.rs @@ -317,7 +317,7 @@ impl<'a> TransferFunctions for LifetimeAnalysis<'a> { .add_edge(Node::Root, Node::Local(*t)); } } - BorrowField(dest, src, _) => { + BorrowField(dest, src, _, _) => { if self.local_types[*src].is_mutable_reference() { after_state.borrow_graph.move_local(*src); } diff --git a/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode.rs b/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode.rs index 496a822ed102..29a42ef0b1f0 100644 --- a/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode.rs +++ b/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use vm::file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, CodeOffset, FieldDefinitionIndex, FunctionHandleIndex, - LocalsSignatureIndex, StructDefinitionIndex, + AddressPoolIndex, ByteArrayPoolIndex, CodeOffset, FunctionHandleIndex, SignatureIndex, + StructDefinitionIndex, }; pub type TempIndex = usize; @@ -22,7 +22,7 @@ pub enum StacklessBytecode { Call( Vec, FunctionHandleIndex, - LocalsSignatureIndex, + Option, Vec, ), /* t1_vec = call(index) with * t2_vec as parameters */ @@ -31,35 +31,35 @@ pub enum StacklessBytecode { Pack( TempIndex, StructDefinitionIndex, - LocalsSignatureIndex, + Option, Vec, ), /* t1 = struct(index) with t2_vec * as fields */ Unpack( Vec, StructDefinitionIndex, - LocalsSignatureIndex, + Option, TempIndex, ), // t1_vec = t2's fields - BorrowField(TempIndex, TempIndex, FieldDefinitionIndex), // t1 = t2.field - MoveToSender(TempIndex, StructDefinitionIndex, LocalsSignatureIndex), /* move_to_sender(t) */ + BorrowField(TempIndex, TempIndex, StructDefinitionIndex, TempIndex), // t1 = t2.field + MoveToSender(TempIndex, StructDefinitionIndex, Option), /* move_to_sender(t) */ MoveFrom( TempIndex, TempIndex, StructDefinitionIndex, - LocalsSignatureIndex, + Option, ), /* t1 = move_from(t2) */ BorrowGlobal( TempIndex, TempIndex, StructDefinitionIndex, - LocalsSignatureIndex, + Option, ), /* t1 = borrow_global(t2) */ Exists( TempIndex, TempIndex, StructDefinitionIndex, - LocalsSignatureIndex, + Option, ), /* t1 = exists(t2) */ GetGasRemaining(TempIndex), diff --git a/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode_generator.rs b/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode_generator.rs index 3b4533472761..070e9f49a8f4 100644 --- a/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode_generator.rs +++ b/language/move-prover/stackless-bytecode-generator/src/stackless_bytecode_generator.rs @@ -9,13 +9,10 @@ use std::collections::BTreeMap; use vm::{ access::ModuleAccess, file_format::{ - Bytecode, CodeOffset, CompiledModule, FieldDefinitionIndex, FunctionDefinition, - LocalsSignatureIndex, SignatureToken, - }, - views::{ - FieldDefinitionView, FunctionDefinitionView, FunctionSignatureView, StructDefinitionView, - ViewInternals, + Bytecode, CodeOffset, CompiledModule, FieldHandleIndex, FunctionDefinition, SignatureIndex, + SignatureToken, StructDefinitionIndex, StructFieldInformation, }, + views::{FunctionDefinitionView, FunctionHandleView, StructDefinitionView, ViewInternals}, }; pub struct StacklessFunction { @@ -61,8 +58,8 @@ impl<'a> StacklessBytecodeGenerator<'a> { if !function_definition_view.is_native() { let locals_signature_view = function_definition_view.locals_signature(); temp_count = locals_signature_view.len(); - for (_, arg_type_view) in locals_signature_view.tokens().enumerate() { - local_types.push(arg_type_view.as_inner().clone()); + for (_, parameter_view) in locals_signature_view.tokens().enumerate() { + local_types.push(parameter_view.as_inner().clone()); } } StacklessBytecodeGenerator { @@ -87,18 +84,24 @@ impl<'a> StacklessBytecodeGenerator<'a> { } } - fn get_field_signature(&self, field_definition_index: FieldDefinitionIndex) -> SignatureToken { - let field_definition = self.module.field_def_at(field_definition_index); - let field_definition_view = FieldDefinitionView::new(self.module, field_definition); - field_definition_view - .type_signature() - .token() - .as_inner() - .clone() + fn get_field_info( + &self, + field_handle_index: FieldHandleIndex, + ) -> (StructDefinitionIndex, usize, SignatureToken) { + let field_handle = self.module.field_handle_at(field_handle_index); + let struct_def = self.module.struct_def_at(field_handle.owner); + if let StructFieldInformation::Declared(fields) = &struct_def.field_information { + return ( + field_handle.owner, + field_handle.field as usize, + fields[field_handle.field as usize].signature.0.clone(), + ); + } + unreachable!("struct must have fields") } - fn get_type_params(&self, type_params_index: LocalsSignatureIndex) -> Vec { - self.module.locals_signature_at(type_params_index).0.clone() + fn get_type_params(&self, type_params_index: SignatureIndex) -> Vec { + self.module.signature_at(type_params_index).0.clone() } #[allow(clippy::cognitive_complexity)] @@ -133,7 +136,7 @@ impl<'a> StacklessBytecodeGenerator<'a> { Bytecode::Ret => { let mut return_temps = vec![]; - for _ in self.function_definition_view.signature().return_tokens() { + for _ in self.function_definition_view.return_tokens() { let return_temp_index = self.temp_stack.pop().unwrap(); return_temps.push(return_temp_index); } @@ -160,52 +163,42 @@ impl<'a> StacklessBytecodeGenerator<'a> { } } - Bytecode::MutBorrowField(field_definition_index) => { + Bytecode::ImmBorrowField(field_handle_index) + | Bytecode::MutBorrowField(field_handle_index) => { let struct_ref_index = self.temp_stack.pop().unwrap(); - let field_signature = self.get_field_signature(*field_definition_index); - let parent_type = self.local_types[struct_ref_index].clone(); - let type_sigs = match parent_type { - SignatureToken::MutableReference(b) => match *b { - SignatureToken::Struct(_, v) => v, - _ => panic!("not a struct in BorrowField"), - }, - _ => panic!("not a reference in BorrowField"), - }; - let field_type = match field_signature { - SignatureToken::TypeParameter(i) => type_sigs[i as usize].clone(), - _ => field_signature, - }; + let (struct_def_idx, field_offset, field_type) = + self.get_field_info(*field_handle_index); let field_ref_index = self.temp_count; self.temp_stack.push(field_ref_index); self.code.push(StacklessBytecode::BorrowField( field_ref_index, struct_ref_index, - *field_definition_index, + struct_def_idx, + field_offset, )); self.temp_count += 1; - self.local_types - .push(SignatureToken::MutableReference(Box::new(field_type))); - } - - Bytecode::ImmBorrowField(field_definition_index) => { - let struct_ref_index = self.temp_stack.pop().unwrap(); - let field_signature = self.get_field_signature(*field_definition_index); - let parent_type = self.local_types[struct_ref_index].clone(); - let type_sigs = match parent_type { - SignatureToken::Reference(b) | SignatureToken::MutableReference(b) => { - match *b { - SignatureToken::Struct(_, v) => v, - _ => panic!("not a struct in BorrowField"), - } + match bytecode { + Bytecode::ImmBorrowField(_) => { + self.local_types + .push(SignatureToken::Reference(Box::new(field_type))); } _ => { - println!("{:?},{:?}", bytecode, parent_type); - panic!("not a reference in BorrowField") + self.local_types + .push(SignatureToken::MutableReference(Box::new(field_type))); } - }; + } + } + + Bytecode::ImmBorrowFieldGeneric(field_inst_index) + | Bytecode::MutBorrowFieldGeneric(field_inst_index) => { + let field_inst = self.module.field_instantiation_at(*field_inst_index); + let struct_ref_index = self.temp_stack.pop().unwrap(); + let (struct_def_idx, field_offset, field_signature) = + self.get_field_info(field_inst.handle); + let type_sigs = self.module.signature_at(field_inst.type_parameters); let field_type = match field_signature { - SignatureToken::TypeParameter(i) => type_sigs[i as usize].clone(), + SignatureToken::TypeParameter(i) => type_sigs.0[i as usize].clone(), _ => field_signature, }; let field_ref_index = self.temp_count; @@ -214,11 +207,20 @@ impl<'a> StacklessBytecodeGenerator<'a> { self.code.push(StacklessBytecode::BorrowField( field_ref_index, struct_ref_index, - *field_definition_index, + struct_def_idx, + field_offset, )); self.temp_count += 1; - self.local_types - .push(SignatureToken::Reference(Box::new(field_type))); + match bytecode { + Bytecode::ImmBorrowField(_) => { + self.local_types + .push(SignatureToken::Reference(Box::new(field_type))); + } + _ => { + self.local_types + .push(SignatureToken::MutableReference(Box::new(field_type))); + } + } } Bytecode::LdU8(number) => { @@ -360,21 +362,47 @@ impl<'a> StacklessBytecodeGenerator<'a> { self.temp_count += 1; } - Bytecode::Call(idx, type_params) => { - let type_sigs = self.get_type_params(*type_params); + Bytecode::Call(idx) => { let function_handle = self.module.function_handle_at(*idx); - let function_signature = - self.module.function_signature_at(function_handle.signature); - let function_signature_view = - FunctionSignatureView::new(self.module, function_signature); + let function_handle_view = FunctionHandleView::new(self.module, function_handle); let mut arg_temp_indices = vec![]; let mut return_temp_indices = vec![]; - for _ in function_signature.arg_types.iter() { + for _ in function_handle_view.arg_tokens() { let arg_temp_index = self.temp_stack.pop().unwrap(); arg_temp_indices.push(arg_temp_index); } - for return_type_view in function_signature_view.return_tokens() { + for return_type_view in function_handle_view.return_tokens() { + let return_temp_index = self.temp_count; + let return_type = return_type_view.as_inner().clone(); + return_temp_indices.push(return_temp_index); + self.temp_stack.push(return_temp_index); + self.local_types.push(return_type); + self.temp_count += 1; + } + arg_temp_indices.reverse(); + return_temp_indices.reverse(); + self.code.push(StacklessBytecode::Call( + return_temp_indices, + *idx, + None, + arg_temp_indices, + )) + } + Bytecode::CallGeneric(idx) => { + let func_instantiation = self.module.function_instantiation_at(*idx); + + let type_sigs = self.get_type_params(func_instantiation.type_parameters); + let function_handle = self.module.function_handle_at(func_instantiation.handle); + let function_handle_view = FunctionHandleView::new(self.module, function_handle); + + let mut arg_temp_indices = vec![]; + let mut return_temp_indices = vec![]; + for _ in function_handle_view.arg_tokens() { + let arg_temp_index = self.temp_stack.pop().unwrap(); + arg_temp_indices.push(arg_temp_index); + } + for return_type_view in function_handle_view.return_tokens() { let return_temp_index = self.temp_count; // instantiate type parameters let return_type = @@ -388,13 +416,13 @@ impl<'a> StacklessBytecodeGenerator<'a> { return_temp_indices.reverse(); self.code.push(StacklessBytecode::Call( return_temp_indices, - *idx, - *type_params, + func_instantiation.handle, + Some(func_instantiation.type_parameters), arg_temp_indices, )) } - Bytecode::Pack(idx, type_params) => { + Bytecode::Pack(idx) => { let struct_definition = self.module.struct_def_at(*idx); let struct_definition_view = StructDefinitionView::new(self.module, struct_definition); @@ -405,28 +433,75 @@ impl<'a> StacklessBytecodeGenerator<'a> { let field_temp_index = self.temp_stack.pop().unwrap(); field_temp_indices.push(field_temp_index); } - self.local_types.push(SignatureToken::Struct( - struct_definition.struct_handle, - self.get_type_params(*type_params), - )); + self.local_types + .push(SignatureToken::Struct(struct_definition.struct_handle)); self.temp_stack.push(struct_temp_index); field_temp_indices.reverse(); self.code.push(StacklessBytecode::Pack( struct_temp_index, *idx, - *type_params, + None, field_temp_indices, )); self.temp_count += 1; } - Bytecode::Unpack(idx, type_params) => { - let type_sigs = self.get_type_params(*type_params); + Bytecode::PackGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let struct_definition = self.module.struct_def_at(struct_instantiation.def); + let struct_definition_view = + StructDefinitionView::new(self.module, struct_definition); + + let mut field_temp_indices = vec![]; + let struct_temp_index = self.temp_count; + for _ in struct_definition_view.fields().unwrap() { + let field_temp_index = self.temp_stack.pop().unwrap(); + field_temp_indices.push(field_temp_index); + } + self.local_types + .push(SignatureToken::Struct(struct_definition.struct_handle)); + self.temp_stack.push(struct_temp_index); + field_temp_indices.reverse(); + self.code.push(StacklessBytecode::Pack( + struct_temp_index, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), + field_temp_indices, + )); + self.temp_count += 1; + } + + Bytecode::Unpack(idx) => { let struct_definition = self.module.struct_def_at(*idx); let struct_definition_view = StructDefinitionView::new(self.module, struct_definition); let mut field_temp_indices = vec![]; let struct_temp_index = self.temp_stack.pop().unwrap(); + for field_definition_view in struct_definition_view.fields().unwrap() { + let field_signature_view = field_definition_view.type_signature(); + let field_type = field_signature_view.token().as_inner().clone(); + let field_temp_index = self.temp_count; + field_temp_indices.push(field_temp_index); + self.temp_stack.push(field_temp_index); + self.local_types.push(field_type); + self.temp_count += 1; + } + self.code.push(StacklessBytecode::Unpack( + field_temp_indices, + *idx, + None, + struct_temp_index, + )); + } + + Bytecode::UnpackGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let type_sigs = self.get_type_params(struct_instantiation.type_parameters); + let struct_definition = self.module.struct_def_at(struct_instantiation.def); + let struct_definition_view = + StructDefinitionView::new(self.module, struct_definition); + let mut field_temp_indices = vec![]; + let struct_temp_index = self.temp_stack.pop().unwrap(); for field_definition_view in struct_definition_view.fields().unwrap() { let field_signature_view = field_definition_view.type_signature(); let field_type = match field_signature_view.token().as_inner() { @@ -441,11 +516,12 @@ impl<'a> StacklessBytecodeGenerator<'a> { } self.code.push(StacklessBytecode::Unpack( field_temp_indices, - *idx, - *type_params, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), struct_temp_index, )); } + Bytecode::ReadRef => { let operand_index = self.temp_stack.pop().unwrap(); let operand_sig = self.local_types[operand_index].clone(); @@ -665,7 +741,7 @@ impl<'a> StacklessBytecodeGenerator<'a> { _ => {} } } - Bytecode::Exists(struct_index, type_params) => { + Bytecode::Exists(struct_index) => { let operand_index = self.temp_stack.pop().unwrap(); let temp_index = self.temp_count; self.local_types.push(SignatureToken::Bool); @@ -675,20 +751,55 @@ impl<'a> StacklessBytecodeGenerator<'a> { temp_index, operand_index, *struct_index, - *type_params, + None, )); } - Bytecode::MutBorrowGlobal(idx, type_params) - | Bytecode::ImmBorrowGlobal(idx, type_params) => { + + Bytecode::ExistsGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let operand_index = self.temp_stack.pop().unwrap(); + let temp_index = self.temp_count; + self.local_types.push(SignatureToken::Bool); + self.temp_count += 1; + self.temp_stack.push(temp_index); + self.code.push(StacklessBytecode::Exists( + temp_index, + operand_index, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), + )); + } + + Bytecode::MutBorrowGlobal(idx) | Bytecode::ImmBorrowGlobal(idx) => { let struct_definition = self.module.struct_def_at(*idx); let operand_index = self.temp_stack.pop().unwrap(); let temp_index = self.temp_count; self.local_types .push(SignatureToken::MutableReference(Box::new( - SignatureToken::Struct( + SignatureToken::Struct(struct_definition.struct_handle), + ))); + self.temp_stack.push(temp_index); + self.temp_count += 1; + self.code.push(StacklessBytecode::BorrowGlobal( + temp_index, + operand_index, + *idx, + None, + )); + } + + Bytecode::MutBorrowGlobalGeneric(idx) | Bytecode::ImmBorrowGlobalGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let struct_definition = self.module.struct_def_at(struct_instantiation.def); + + let operand_index = self.temp_stack.pop().unwrap(); + let temp_index = self.temp_count; + self.local_types + .push(SignatureToken::MutableReference(Box::new( + SignatureToken::StructInstantiation( struct_definition.struct_handle, - self.get_type_params(*type_params), + self.get_type_params(struct_instantiation.type_parameters), ), ))); self.temp_stack.push(temp_index); @@ -696,33 +807,62 @@ impl<'a> StacklessBytecodeGenerator<'a> { self.code.push(StacklessBytecode::BorrowGlobal( temp_index, operand_index, - *idx, - *type_params, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), )); } - Bytecode::MoveFrom(idx, type_params) => { + + Bytecode::MoveFrom(idx) => { let struct_definition = self.module.struct_def_at(*idx); let operand_index = self.temp_stack.pop().unwrap(); let temp_index = self.temp_count; self.temp_stack.push(temp_index); - self.local_types.push(SignatureToken::Struct( + self.local_types + .push(SignatureToken::Struct(struct_definition.struct_handle)); + self.temp_count += 1; + self.code.push(StacklessBytecode::MoveFrom( + temp_index, + operand_index, + *idx, + None, + )); + } + + Bytecode::MoveFromGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let struct_definition = self.module.struct_def_at(struct_instantiation.def); + let operand_index = self.temp_stack.pop().unwrap(); + let temp_index = self.temp_count; + self.temp_stack.push(temp_index); + self.local_types.push(SignatureToken::StructInstantiation( struct_definition.struct_handle, - self.get_type_params(*type_params), + self.get_type_params(struct_instantiation.type_parameters), )); self.temp_count += 1; self.code.push(StacklessBytecode::MoveFrom( temp_index, operand_index, - *idx, - *type_params, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), )); } - Bytecode::MoveToSender(idx, type_params) => { + + Bytecode::MoveToSender(idx) => { let value_operand_index = self.temp_stack.pop().unwrap(); self.code.push(StacklessBytecode::MoveToSender( value_operand_index, *idx, - *type_params, + None, + )); + } + + Bytecode::MoveToSenderGeneric(idx) => { + let struct_instantiation = self.module.struct_instantiation_at(*idx); + let value_operand_index = self.temp_stack.pop().unwrap(); + self.code.push(StacklessBytecode::MoveToSender( + value_operand_index, + struct_instantiation.def, + Some(struct_instantiation.type_parameters), )); } @@ -759,13 +899,15 @@ impl<'a> StacklessBytecodeGenerator<'a> { SignatureToken::MutableReference(b) => SignatureToken::MutableReference(Box::new( self.instantiate_type_params(&**b, actuals), )), - SignatureToken::Struct(handle_index, type_args) => SignatureToken::Struct( - *handle_index, - type_args - .iter() - .map(|a| self.instantiate_type_params(a, actuals)) - .collect(), - ), + SignatureToken::StructInstantiation(handle_index, type_args) => { + SignatureToken::StructInstantiation( + *handle_index, + type_args + .iter() + .map(|a| self.instantiate_type_params(a, actuals)) + .collect(), + ) + } _ => sig.clone(), } } @@ -827,8 +969,13 @@ impl<'a> StacklessBytecodeGenerator<'a> { let new_dest_vec = dest_vec.iter().map(|t| temp_to_local(t)).collect(); new_code.push(Unpack(new_dest_vec, *s, *l, temp_to_local(src))); } - BorrowField(dest, src, f) => { - new_code.push(BorrowField(temp_to_local(dest), temp_to_local(src), *f)); + BorrowField(dest, src, s, offset) => { + new_code.push(BorrowField( + temp_to_local(dest), + temp_to_local(src), + *s, + *offset, + )); } MoveToSender(t, s, l) => { new_code.push(MoveToSender(temp_to_local(t), *s, *l)); diff --git a/language/move-prover/stackless-bytecode-generator/tests/stack_elim_tests.rs b/language/move-prover/stackless-bytecode-generator/tests/stack_elim_tests.rs index 90677f634866..3bbc10521e96 100644 --- a/language/move-prover/stackless-bytecode-generator/tests/stack_elim_tests.rs +++ b/language/move-prover/stackless-bytecode-generator/tests/stack_elim_tests.rs @@ -12,8 +12,8 @@ use stackless_bytecode_generator::{ }; use stdlib::{stdlib_modules, StdLibOptions}; use vm::file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, FieldDefinitionIndex, FunctionHandleIndex, - LocalsSignatureIndex, SignatureToken, StructDefinitionIndex, StructHandleIndex, + AddressPoolIndex, ByteArrayPoolIndex, FunctionHandleIndex, SignatureIndex, SignatureToken, + StructDefinitionIndex, StructHandleIndex, }; #[test] @@ -42,7 +42,7 @@ fn transform_code_with_refs() { MoveLoc(6, 1), WriteRef(6, 5), MoveLoc(7, 0), - BorrowField(8, 7, FieldDefinitionIndex::new(0)), + BorrowField(8, 7, StructDefinitionIndex::new(0), 0), StLoc(3, 8), MoveLoc(9, 2), FreezeRef(10, 9), @@ -54,39 +54,22 @@ fn transform_code_with_refs() { Ret(vec![13]), ]; let expected_types = vec![ - SignatureToken::Reference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::Reference(Box::new(SignatureToken::Struct(StructHandleIndex::new(0)))), SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::MutableReference(Box::new(SignatureToken::Struct(StructHandleIndex::new( + 0, + )))), SignatureToken::Reference(Box::new(SignatureToken::U64)), - SignatureToken::Reference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::Reference(Box::new(SignatureToken::Struct(StructHandleIndex::new(0)))), SignatureToken::U64, SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - SignatureToken::Reference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::Reference(Box::new(SignatureToken::Struct(StructHandleIndex::new(0)))), SignatureToken::Reference(Box::new(SignatureToken::U64)), - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), - SignatureToken::Reference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), - SignatureToken::Reference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::MutableReference(Box::new(SignatureToken::Struct(StructHandleIndex::new( + 0, + )))), + SignatureToken::Reference(Box::new(SignatureToken::Struct(StructHandleIndex::new(0)))), + SignatureToken::Reference(Box::new(SignatureToken::Struct(StructHandleIndex::new(0)))), SignatureToken::Reference(Box::new(SignatureToken::U64)), SignatureToken::U64, ]; @@ -163,33 +146,23 @@ fn transform_code_with_pack_unpack() { let expected_code = vec![ LdU64(4, 42), MoveLoc(5, 0), - Pack( - 6, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - vec![4, 5], - ), + Pack(6, StructDefinitionIndex::new(0), None, vec![4, 5]), StLoc(1, 6), MoveLoc(7, 1), - Unpack( - vec![8, 9], - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - 7, - ), + Unpack(vec![8, 9], StructDefinitionIndex::new(0), None, 7), StLoc(3, 9), StLoc(2, 8), Ret(vec![]), ]; let expected_types = vec![ SignatureToken::Address, - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), + SignatureToken::Struct(StructHandleIndex::new(0)), SignatureToken::U64, SignatureToken::Address, SignatureToken::U64, SignatureToken::Address, - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), + SignatureToken::Struct(StructHandleIndex::new(0)), + SignatureToken::Struct(StructHandleIndex::new(0)), SignatureToken::U64, SignatureToken::Address, ]; @@ -412,7 +385,7 @@ fn transform_code_with_function_call() { Call( vec![11, 10, 9], FunctionHandleIndex::new(1), - LocalsSignatureIndex::new(1), + None, vec![6, 7, 8], ), StLoc(5, 11), @@ -464,60 +437,38 @@ fn transform_code_with_module_builtins() { let (actual_code, actual_types) = generate_module_from_string(code); let expected_code = vec![ CopyLoc(4, 0), - Exists( - 5, - 4, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - ), + Exists(5, 4, StructDefinitionIndex::new(0), None), StLoc(3, 5), CopyLoc(6, 0), - BorrowGlobal( - 7, - 6, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - ), + BorrowGlobal(7, 6, StructDefinitionIndex::new(0), None), StLoc(2, 7), CopyLoc(8, 0), - MoveFrom( - 9, - 8, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - ), + MoveFrom(9, 8, StructDefinitionIndex::new(0), None), StLoc(1, 9), MoveLoc(10, 1), - MoveToSender( - 10, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - ), + MoveToSender(10, StructDefinitionIndex::new(0), None), MoveLoc(11, 2), Ret(vec![11]), ]; let expected_types = vec![ SignatureToken::Address, - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::Struct(StructHandleIndex::new(0)), + SignatureToken::MutableReference(Box::new(SignatureToken::Struct(StructHandleIndex::new( + 0, + )))), SignatureToken::Bool, SignatureToken::Address, SignatureToken::Bool, SignatureToken::Address, - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::MutableReference(Box::new(SignatureToken::Struct(StructHandleIndex::new( + 0, + )))), SignatureToken::Address, - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( - StructHandleIndex::new(0), - vec![], - ))), + SignatureToken::Struct(StructHandleIndex::new(0)), + SignatureToken::Struct(StructHandleIndex::new(0)), + SignatureToken::MutableReference(Box::new(SignatureToken::Struct(StructHandleIndex::new( + 0, + )))), ]; assert_eq!(actual_code, expected_code); assert_eq!(actual_types, expected_types); @@ -539,12 +490,7 @@ fn transform_program_with_script() { MoveLoc(3, 0), MoveLoc(4, 1), MoveLoc(5, 2), - Call( - vec![], - FunctionHandleIndex::new(1), - LocalsSignatureIndex::new(1), - vec![3, 4, 5], - ), + Call(vec![], FunctionHandleIndex::new(1), None, vec![3, 4, 5]), Ret(vec![]), ]; let expected_types = vec![ @@ -581,7 +527,7 @@ fn transform_program_with_generics() { let (actual_code, actual_types) = generate_module_from_string(code); let expected_code = vec![ BorrowLoc(4, 0), - BorrowField(5, 4, FieldDefinitionIndex::new(0)), + BorrowField(5, 4, StructDefinitionIndex::new(0), 0), StLoc(2, 5), MoveLoc(6, 2), Pop(6), @@ -589,7 +535,7 @@ fn transform_program_with_generics() { Unpack( vec![8], StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), + Some(SignatureIndex::new(3)), 7, ), StLoc(3, 8), @@ -597,17 +543,17 @@ fn transform_program_with_generics() { Ret(vec![9]), ]; let expected_types = vec![ - SignatureToken::Struct(StructHandleIndex::new(0), vec![SignatureToken::U64]), + SignatureToken::StructInstantiation(StructHandleIndex::new(0), vec![SignatureToken::U64]), SignatureToken::TypeParameter(0), SignatureToken::MutableReference(Box::new(SignatureToken::U64)), SignatureToken::U64, - SignatureToken::MutableReference(Box::new(SignatureToken::Struct( + SignatureToken::MutableReference(Box::new(SignatureToken::StructInstantiation( StructHandleIndex::new(0), vec![SignatureToken::U64], ))), SignatureToken::MutableReference(Box::new(SignatureToken::U64)), SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - SignatureToken::Struct(StructHandleIndex::new(0), vec![SignatureToken::U64]), + SignatureToken::StructInstantiation(StructHandleIndex::new(0), vec![SignatureToken::U64]), SignatureToken::U64, SignatureToken::TypeParameter(0), ]; @@ -658,25 +604,15 @@ fn transform_and_simplify() { // simplified bytecode without unnecessary moves let expected_simplified_code = vec![ LdU64(6, 3), - Pack( - 2, - StructDefinitionIndex::new(0), - LocalsSignatureIndex::new(1), - vec![6], - ), + Pack(2, StructDefinitionIndex::new(0), None, vec![6]), LdU64(8, 4), - Pack( - 4, - StructDefinitionIndex::new(1), - LocalsSignatureIndex::new(1), - vec![8], - ), + Pack(4, StructDefinitionIndex::new(1), None, vec![8]), BorrowLoc(1, 2), BorrowLoc(3, 4), BrFalse(9, 0), - BorrowField(5, 1, FieldDefinitionIndex::new(0)), + BorrowField(5, 1, StructDefinitionIndex::new(0), 0), Branch(10), - BorrowField(5, 3, FieldDefinitionIndex::new(1)), + BorrowField(5, 3, StructDefinitionIndex::new(1), 0), LdU64(17, 10), WriteRef(5, 17), Ret(vec![]), diff --git a/language/move-vm/runtime/src/code_cache/module_cache.rs b/language/move-vm/runtime/src/code_cache/module_cache.rs index 7a218389e529..e889f427c2e2 100644 --- a/language/move-vm/runtime/src/code_cache/module_cache.rs +++ b/language/move-vm/runtime/src/code_cache/module_cache.rs @@ -100,26 +100,19 @@ impl<'alloc> VMModuleCache<'alloc> { let struct_handle = module.struct_handle_at(struct_def.struct_handle); let struct_name = module.identifier_at(struct_handle.name); - let (field_count, fields) = match &struct_def.field_information { + let fields = match &struct_def.field_information { StructFieldInformation::Native => unreachable!("native structs have been removed"), - StructFieldInformation::Declared { - field_count, - fields, - } => (*field_count, *fields), + StructFieldInformation::Declared(fields) => fields, }; - let ty_args: Vec<_> = (0..struct_handle.type_formals.len()) + let ty_args: Vec<_> = (0..struct_handle.type_parameters.len()) .map(|idx| Type::TyParam(idx as usize)) .collect(); let mut field_tys = vec![]; - for field in module.field_def_range(field_count, fields) { - let ty = self.resolve_signature_token( - module, - &module.type_signature_at(field.signature).0, - &ty_args, - data_view, - )?; + for field in fields { + let ty = + self.resolve_signature_token(module, &field.signature.0, &ty_args, data_view)?; // `field_types` is initally empty, a single element is pushed // per loop iteration and the number of iterations is bound to // the max size of `module.field_def_range()`. @@ -251,7 +244,11 @@ impl<'alloc> VMModuleCache<'alloc> { let inner_ty = self.resolve_signature_token(module, sub_tok, ty_args, data_view)?; Type::MutableReference(Box::new(inner_ty)) } - SignatureToken::Struct(sh_idx, tys) => { + SignatureToken::Struct(sh_idx) => { + let struct_ty = self.resolve_struct_handle(module, *sh_idx, &[], data_view)?; + Type::Struct(Box::new(struct_ty)) + } + SignatureToken::StructInstantiation(sh_idx, tys) => { let ty_args: Vec<_> = tys .iter() .map(|tok| self.resolve_signature_token(module, tok, ty_args, data_view)) diff --git a/language/move-vm/runtime/src/interpreter.rs b/language/move-vm/runtime/src/interpreter.rs index 1301430dbb43..a8495be9e8c1 100644 --- a/language/move-vm/runtime/src/interpreter.rs +++ b/language/move-vm/runtime/src/interpreter.rs @@ -37,7 +37,8 @@ use vm::{ access::ModuleAccess, errors::*, file_format::{ - Bytecode, FunctionHandleIndex, LocalIndex, LocalsSignatureIndex, StructDefinitionIndex, + Bytecode, FunctionHandleIndex, FunctionInstantiationIndex, LocalIndex, Signature, + StructDefinitionIndex, }, gas_schedule::{ calculate_intrinsic_gas, AbstractMemorySize, CostTable, GasAlgebra, GasCarrier, @@ -160,12 +161,34 @@ impl<'txn> Interpreter<'txn> { return Err(self.unreachable("call stack cannot be empty", ¤t_frame)); } } - ExitCode::Call(idx, ty_args_idx) => { - let ty_arg_sigs = ¤t_frame.module().locals_signature_at(ty_args_idx).0; + ExitCode::Call(fh_idx) => { gas!( instr: context, self, - Opcodes::CALL, + Opcodes::CALL_GENERIC, + AbstractMemorySize::new(1 as GasCarrier) + )?; + let opt_frame = self + .make_call_frame(runtime, context, current_frame.module(), fh_idx, vec![]) + .or_else(|err| Err(self.maybe_core_dump(err, ¤t_frame)))?; + if let Some(frame) = opt_frame { + self.call_stack.push(current_frame).or_else(|frame| { + let err = VMStatus::new(StatusCode::CALL_STACK_OVERFLOW); + Err(self.maybe_core_dump(err, &frame)) + })?; + current_frame = frame; + } + } + ExitCode::CallGeneric(idx) => { + let func_inst = ¤t_frame.module().function_instantiation_at(idx); + let ty_arg_sigs = ¤t_frame + .module() + .signature_at(func_inst.type_parameters) + .0; + gas!( + instr: context, + self, + Opcodes::CALL_GENERIC, AbstractMemorySize::new((ty_arg_sigs.len() + 1) as GasCarrier) )?; let ty_args = ty_arg_sigs @@ -181,7 +204,13 @@ impl<'txn> Interpreter<'txn> { .collect::>>()?; let opt_frame = self - .make_call_frame(runtime, context, current_frame.module(), idx, ty_args) + .make_call_frame( + runtime, + context, + current_frame.module(), + func_inst.handle, + ty_args, + ) .or_else(|err| Err(self.maybe_core_dump(err, ¤t_frame)))?; if let Some(frame) = opt_frame { self.call_stack.push(current_frame).or_else(|frame| { @@ -287,8 +316,11 @@ impl<'txn> Interpreter<'txn> { gas!(instr: context, self, Opcodes::ST_LOC, value_to_store.size())?; frame.store_loc(*idx, value_to_store)?; } - Bytecode::Call(idx, type_actuals_idx) => { - return Ok(ExitCode::Call(*idx, *type_actuals_idx)); + Bytecode::Call(idx) => { + return Ok(ExitCode::Call(*idx)); + } + Bytecode::CallGeneric(idx) => { + return Ok(ExitCode::CallGeneric(*idx)); } Bytecode::MutBorrowLoc(idx) | Bytecode::ImmBorrowLoc(idx) => { let opcode = match instruction { @@ -304,12 +336,27 @@ impl<'txn> Interpreter<'txn> { _ => Opcodes::IMM_BORROW_FIELD, }; gas!(const_instr: context, self, opcode)?; - let field_offset = frame.module().get_field_offset(*fd_idx)?; + let field_handle = frame.module().field_handle_at(*fd_idx); + let field_offset = field_handle.field; let reference = self.operand_stack.pop_as::()?; let field_ref = reference.borrow_field(field_offset as usize)?; self.operand_stack.push(field_ref)?; } - Bytecode::Pack(sd_idx, _) => { + Bytecode::ImmBorrowFieldGeneric(fi_idx) + | Bytecode::MutBorrowFieldGeneric(fi_idx) => { + let field_inst = frame.module().field_instantiation_at(*fi_idx); + let field_handle = frame.module().field_handle_at(field_inst.handle); + let opcode = match instruction { + Bytecode::MutBorrowField(_) => Opcodes::MUT_BORROW_FIELD_GENERIC, + _ => Opcodes::IMM_BORROW_FIELD_GENERIC, + }; + gas!(const_instr: context, self, opcode)?; + let field_offset = field_handle.field; + let reference = self.operand_stack.pop_as::()?; + let field_ref = reference.borrow_field(field_offset as usize)?; + self.operand_stack.push(field_ref)?; + } + Bytecode::Pack(sd_idx) => { let struct_def = frame.module().struct_def_at(*sd_idx); let field_count = struct_def.declared_field_count()?; let args = self.operand_stack.popn(field_count)?; @@ -321,7 +368,20 @@ impl<'txn> Interpreter<'txn> { self.operand_stack .push(Value::struct_(Struct::pack(args)))?; } - Bytecode::Unpack(sd_idx, _) => { + Bytecode::PackGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let struct_def = frame.module().struct_def_at(struct_inst.def); + let field_count = struct_def.declared_field_count()?; + let args = self.operand_stack.popn(field_count)?; + let size = args.iter().fold( + AbstractMemorySize::new(GasCarrier::from(field_count)), + |acc, v| acc.add(v.size()), + ); + gas!(instr: context, self, Opcodes::PACK_GENERIC, size)?; + self.operand_stack + .push(Value::struct_(Struct::pack(args)))?; + } + Bytecode::Unpack(sd_idx) => { let struct_def = frame.module().struct_def_at(*sd_idx); let field_count = struct_def.declared_field_count()?; let struct_ = self.operand_stack.pop_as::()?; @@ -339,6 +399,25 @@ impl<'txn> Interpreter<'txn> { self.operand_stack.push(value)?; } } + Bytecode::UnpackGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let struct_def = frame.module().struct_def_at(struct_inst.def); + let field_count = struct_def.declared_field_count()?; + let struct_ = self.operand_stack.pop_as::()?; + gas!( + instr: context, + self, + Opcodes::UNPACK_GENERIC, + AbstractMemorySize::new(GasCarrier::from(field_count)) + )?; + // TODO: Whether or not we want this gas metering in the loop is + // questionable. However, if we don't have it in the loop we could wind up + // doing a fair bit of work before charging for it. + for value in struct_.unpack()? { + gas!(instr: context, self, Opcodes::UNPACK_GENERIC, value.size())?; + self.operand_stack.push(value)?; + } + } Bytecode::ReadRef => { let reference = self.operand_stack.pop_as::()?; let value = reference.read_ref()?; @@ -472,41 +551,78 @@ impl<'txn> Interpreter<'txn> { self.operand_stack .push(Value::address(self.txn_data.sender()))?; } - Bytecode::MutBorrowGlobal(idx, type_actuals_idx) - | Bytecode::ImmBorrowGlobal(idx, type_actuals_idx) => { + Bytecode::MutBorrowGlobal(sd_idx) | Bytecode::ImmBorrowGlobal(sd_idx) => { let addr = self.operand_stack.pop_as::()?; let size = self.global_data_op( runtime, context, addr, - *idx, - *type_actuals_idx, + *sd_idx, + &Signature(vec![]), frame, Self::borrow_global, )?; gas!(instr: context, self, Opcodes::MUT_BORROW_GLOBAL, size)?; } - Bytecode::Exists(idx, type_actuals_idx) => { + Bytecode::MutBorrowGlobalGeneric(si_idx) + | Bytecode::ImmBorrowGlobalGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let instantiation = + frame.module().signature_at(struct_inst.type_parameters); let addr = self.operand_stack.pop_as::()?; let size = self.global_data_op( runtime, context, addr, - *idx, - *type_actuals_idx, + struct_inst.def, + instantiation, + frame, + Self::borrow_global, + )?; + gas!( + instr: context, + self, + Opcodes::MUT_BORROW_GLOBAL_GENERIC, + size + )?; + } + Bytecode::Exists(sd_idx) => { + let addr = self.operand_stack.pop_as::()?; + let size = self.global_data_op( + runtime, + context, + addr, + *sd_idx, + &Signature(vec![]), frame, Self::exists, )?; gas!(instr: context, self, Opcodes::EXISTS, size)?; } - Bytecode::MoveFrom(idx, type_actuals_idx) => { + Bytecode::ExistsGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let instantiation = + frame.module().signature_at(struct_inst.type_parameters); let addr = self.operand_stack.pop_as::()?; let size = self.global_data_op( runtime, context, addr, - *idx, - *type_actuals_idx, + struct_inst.def, + instantiation, + frame, + Self::exists, + )?; + gas!(instr: context, self, Opcodes::EXISTS_GENERIC, size)?; + } + Bytecode::MoveFrom(sd_idx) => { + let addr = self.operand_stack.pop_as::()?; + let size = self.global_data_op( + runtime, + context, + addr, + *sd_idx, + &Signature(vec![]), frame, Self::move_from, )?; @@ -514,19 +630,53 @@ impl<'txn> Interpreter<'txn> { // the size of the data that we are about to read in. gas!(instr: context, self, Opcodes::MOVE_FROM, size)?; } - Bytecode::MoveToSender(idx, type_actuals_idx) => { + Bytecode::MoveFromGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let instantiation = + frame.module().signature_at(struct_inst.type_parameters); + let addr = self.operand_stack.pop_as::()?; + let size = self.global_data_op( + runtime, + context, + addr, + struct_inst.def, + instantiation, + frame, + Self::move_from, + )?; + // TODO: Have this calculate before pulling in the data based upon + // the size of the data that we are about to read in. + gas!(instr: context, self, Opcodes::MOVE_FROM_GENERIC, size)?; + } + Bytecode::MoveToSender(sd_idx) => { let addr = self.txn_data.sender(); let size = self.global_data_op( runtime, context, addr, - *idx, - *type_actuals_idx, + *sd_idx, + &Signature(vec![]), frame, Self::move_to_sender, )?; gas!(instr: context, self, Opcodes::MOVE_TO, size)?; } + Bytecode::MoveToSenderGeneric(si_idx) => { + let struct_inst = frame.module().struct_instantiation_at(*si_idx); + let instantiation = + frame.module().signature_at(struct_inst.type_parameters); + let addr = self.txn_data.sender(); + let size = self.global_data_op( + runtime, + context, + addr, + struct_inst.def, + instantiation, + frame, + Self::move_to_sender, + )?; + gas!(instr: context, self, Opcodes::MOVE_TO_GENERIC, size)?; + } Bytecode::FreezeRef => { // FreezeRef should just be a null op as we don't distinguish between mut // and immut ref at runtime. @@ -753,7 +903,7 @@ impl<'txn> Interpreter<'txn> { context: &mut dyn InterpreterContext, address: AccountAddress, idx: StructDefinitionIndex, - type_actuals_idx: LocalsSignatureIndex, + ty_arg_sigs: &Signature, frame: &Frame<'txn, FunctionRef<'txn>>, op: F, ) -> VMResult> @@ -766,8 +916,8 @@ impl<'txn> Interpreter<'txn> { ) -> VMResult>, { let module = frame.module(); - let ty_arg_sigs = &frame.module().locals_signature_at(type_actuals_idx).0; let ty_args = ty_arg_sigs + .0 .iter() .map(|tok| { runtime.resolve_signature_token(frame.module(), tok, frame.ty_args(), context) @@ -1025,10 +1175,9 @@ struct Frame<'txn, F: 'txn> { /// An `ExitCode` from `execute_code_unit`. #[derive(Debug)] enum ExitCode { - /// A `Return` opcode was found. Return, - /// A `Call` opcode was found. - Call(FunctionHandleIndex, LocalsSignatureIndex), + Call(FunctionHandleIndex), + CallGeneric(FunctionInstantiationIndex), } impl<'txn, F> Frame<'txn, F> diff --git a/language/move-vm/runtime/src/loaded_data/function.rs b/language/move-vm/runtime/src/loaded_data/function.rs index 287220b64f21..b6567a1483cb 100644 --- a/language/move-vm/runtime/src/loaded_data/function.rs +++ b/language/move-vm/runtime/src/loaded_data/function.rs @@ -7,7 +7,7 @@ use bytecode_verifier::VerifiedModule; use move_core_types::identifier::IdentStr; use vm::{ access::ModuleAccess, - file_format::{Bytecode, CodeUnit, FunctionDefinitionIndex, FunctionHandle, FunctionSignature}, + file_format::{Bytecode, CodeUnit, FunctionDefinitionIndex, FunctionHandle, Kind, Signature}, internals::ModuleIndex, }; @@ -37,8 +37,14 @@ pub trait FunctionReference<'txn>: Sized + Clone { /// Return the name of the function fn name(&self) -> &'txn IdentStr; - /// Returns the signature of the function - fn signature(&self) -> &'txn FunctionSignature; + /// Returns the parameters of the function + fn parameters(&self) -> &'txn Signature; + + /// Returns the "return parameters" of the function + fn return_(&self) -> &'txn Signature; + + /// Returns the type parameters of the function + fn type_parameters(&self) -> &'txn [Kind]; } /// Resolved form of a function handle @@ -89,20 +95,29 @@ impl<'txn> FunctionReference<'txn> for FunctionRef<'txn> { self.module.identifier_at(self.handle.name) } - fn signature(&self) -> &'txn FunctionSignature { - self.module.function_signature_at(self.handle.signature) + fn parameters(&self) -> &'txn Signature { + self.module.signature_at(self.handle.parameters) + } + + fn return_(&self) -> &'txn Signature { + self.module.signature_at(self.handle.return_) + } + + fn type_parameters(&self) -> &'txn [Kind] { + &self.handle.type_parameters } } impl<'txn> FunctionRef<'txn> { pub fn pretty_string(&self) -> String { - let signature = self.signature(); + let parameters = self.parameters(); + let return_ = self.parameters(); format!( "{}::{}({:?}){:?}", self.module().name(), self.name().as_str(), - signature.arg_types, - signature.return_types + parameters, + return_ ) } } @@ -122,19 +137,20 @@ impl FunctionDef { let definition = module.function_def_at(idx); let code = definition.code.code.clone(); let handle = module.function_handle_at(definition.function); - let function_sig = module.function_signature_at(handle.signature); + let parameters = module.signature_at(handle.parameters); + let return_ = module.signature_at(handle.return_); let flags = definition.flags; FunctionDef { code, flags, - arg_count: function_sig.arg_types.len(), - return_count: function_sig.return_types.len(), + arg_count: parameters.len(), + return_count: return_.len(), // Local count for native function is omitted local_count: if (flags & CodeUnit::NATIVE) == CodeUnit::NATIVE { 0 } else { - module.locals_signature_at(definition.code.locals).0.len() + module.signature_at(definition.code.locals).0.len() }, } } diff --git a/language/move-vm/runtime/src/loaded_data/loaded_module.rs b/language/move-vm/runtime/src/loaded_data/loaded_module.rs index acfcea5730c1..5f7be64b4c70 100644 --- a/language/move-vm/runtime/src/loaded_data/loaded_module.rs +++ b/language/move-vm/runtime/src/loaded_data/loaded_module.rs @@ -11,10 +11,7 @@ use std::{collections::HashMap, sync::RwLock}; use vm::{ access::ModuleAccess, errors::VMResult, - file_format::{ - CompiledModule, FieldDefinitionIndex, FunctionDefinitionIndex, StructDefinitionIndex, - StructFieldInformation, TableIndex, - }, + file_format::{CompiledModule, FunctionDefinitionIndex, StructDefinitionIndex, TableIndex}, internals::ModuleIndex, }; @@ -24,15 +21,8 @@ use vm::{ pub struct LoadedModule { module: VerifiedModule, pub struct_defs_table: HashMap, - #[allow(dead_code)] - pub field_defs_table: HashMap, - pub function_defs_table: HashMap, - pub function_defs: Vec, - - pub field_offsets: Vec, - cache: LoadedModuleCache, } @@ -61,7 +51,6 @@ impl Eq for LoadedModuleCache {} impl LoadedModule { pub fn new(module: VerifiedModule) -> Self { let mut struct_defs_table = HashMap::new(); - let mut field_defs_table = HashMap::new(); let mut function_defs_table = HashMap::new(); let mut function_defs = vec![]; @@ -72,32 +61,12 @@ impl LoadedModule { .collect(); let cache = LoadedModuleCache { struct_defs }; - let mut field_offsets: Vec = module.field_defs().iter().map(|_| 0).collect(); - for (idx, struct_def) in module.struct_defs().iter().enumerate() { let name = module .identifier_at(module.struct_handle_at(struct_def.struct_handle).name) .into(); let sd_idx = StructDefinitionIndex::new(idx as TableIndex); struct_defs_table.insert(name, sd_idx); - - if let StructFieldInformation::Declared { - field_count, - fields, - } = &struct_def.field_information - { - for i in 0..*field_count { - let field_index = fields.into_index(); - // Implication of module verification `member_struct_defs` check - assume!(field_index <= usize::max_value() - (i as usize)); - field_offsets[field_index + (i as usize)] = i; - } - } - } - for (idx, field_def) in module.field_defs().iter().enumerate() { - let name = module.identifier_at(field_def.name).into(); - let fd_idx = FieldDefinitionIndex::new(idx as TableIndex); - field_defs_table.insert(name, fd_idx); } for (idx, function_def) in module.function_defs().iter().enumerate() { @@ -116,10 +85,8 @@ impl LoadedModule { LoadedModule { module, struct_defs_table, - field_defs_table, function_defs_table, function_defs, - field_offsets, cache, } } @@ -142,13 +109,6 @@ impl LoadedModule { cached.replace(ty); } - pub fn get_field_offset(&self, idx: FieldDefinitionIndex) -> VMResult { - self.field_offsets - .get(idx.into_index()) - .cloned() - .ok_or_else(|| VMStatus::new(StatusCode::LINKER_ERROR)) - } - pub fn get_struct_def_index(&self, struct_name: &IdentStr) -> VMResult<&StructDefinitionIndex> { self.struct_defs_table .get(struct_name) diff --git a/language/move-vm/runtime/src/runtime.rs b/language/move-vm/runtime/src/runtime.rs index 704468c716a2..ab22d911cae7 100644 --- a/language/move-vm/runtime/src/runtime.rs +++ b/language/move-vm/runtime/src/runtime.rs @@ -25,9 +25,7 @@ use move_vm_types::{ use vm::{ access::ModuleAccess, errors::{verification_error, vm_error, Location, VMResult}, - file_format::{ - FunctionHandleIndex, FunctionSignature, Kind, SignatureToken, StructDefinitionIndex, - }, + file_format::{FunctionHandleIndex, Kind, Signature, SignatureToken, StructDefinitionIndex}, gas_schedule::CostTable, transaction_metadata::TransactionMetadata, CompiledModule, IndexKind, @@ -118,8 +116,8 @@ impl<'alloc> VMRuntime<'alloc> { ) -> VMResult<()> { let main = self.script_cache.cache_script(&script, context)?; - verify_ty_args(&main.signature().type_formals, &ty_args)?; - verify_args(main.signature(), &args)?; + verify_ty_args(&main.type_parameters(), &ty_args)?; + verify_args(main.parameters(), &args)?; Interpreter::entrypoint(context, self, txn_data, gas_schedule, main, ty_args, args) } @@ -141,9 +139,8 @@ impl<'alloc> VMRuntime<'alloc> { .ok_or_else(|| VMStatus::new(StatusCode::LINKER_ERROR))?; let func_def = loaded_module.function_def_at(*func_idx); let func_handle = loaded_module.function_handle_at(func_def.function); - let func_sig = loaded_module.function_signature_at(func_handle.signature); - verify_ty_args(&func_sig.type_formals, &ty_args).map_err(|status| { + verify_ty_args(&func_handle.type_parameters, &ty_args).map_err(|status| { VMStatus::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).append(status) })?; @@ -175,7 +172,7 @@ impl<'alloc> VMRuntime<'alloc> { let struct_idx = module.get_struct_def_index(name)?; let def = module.struct_def_at(*struct_idx); let handle = module.struct_handle_at(def.struct_handle); - verify_ty_args(&handle.type_formals, ty_args)?; + verify_ty_args(&handle.type_parameters, ty_args)?; self.code_cache .resolve_struct_def(module, *struct_idx, ty_args, context) } @@ -223,13 +220,13 @@ impl<'alloc> VMRuntime<'alloc> { fn verify_ty_args(constraints: &[Kind], ty_args: &[Type]) -> VMResult<()> { if constraints.len() != ty_args.len() { - return Err(VMStatus::new(StatusCode::NUMBER_OF_TYPE_ACTUALS_MISMATCH)); + return Err(VMStatus::new(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH)); } for (ty, expected_k) in ty_args.iter().zip(constraints) { let k = if ty.is_resource()? { Kind::Resource } else { - Kind::Unrestricted + Kind::Copyable }; if !k.is_sub_kind_of(*expected_k) { return Err(VMStatus::new(StatusCode::CONTRAINT_KIND_MISMATCH)); @@ -239,17 +236,17 @@ fn verify_ty_args(constraints: &[Kind], ty_args: &[Type]) -> VMResult<()> { } /// Verify if the transaction arguments match the type signature of the main function. -fn verify_args(signature: &FunctionSignature, args: &[Value]) -> VMResult<()> { - if signature.arg_types.len() != args.len() { +fn verify_args(signature: &Signature, args: &[Value]) -> VMResult<()> { + if signature.len() != args.len() { return Err( VMStatus::new(StatusCode::TYPE_MISMATCH).with_message(format!( "argument length mismatch: expected {} got {}", - signature.arg_types.len(), + signature.len(), args.len() )), ); } - for (arg, ty) in args.iter().zip(signature.arg_types.iter()) { + for (arg, ty) in args.iter().zip(signature.0.iter()) { if !arg.is_valid_script_arg(ty) { return Err(VMStatus::new(StatusCode::TYPE_MISMATCH) .with_message("argument type mismatch".to_string())); diff --git a/language/move-vm/runtime/src/unit_tests/module_cache_tests.rs b/language/move-vm/runtime/src/unit_tests/module_cache_tests.rs index c0b47ee1a297..f979eeeb73c2 100644 --- a/language/move-vm/runtime/src/unit_tests/module_cache_tests.rs +++ b/language/move-vm/runtime/src/unit_tests/module_cache_tests.rs @@ -77,64 +77,55 @@ impl RemoteCache for FakeDataCache { fn test_module(name: &'static str) -> VerifiedModule { let compiled_module = CompiledModuleMut { module_handles: vec![ModuleHandle { - name: IdentifierIndex::new(0), - address: AddressPoolIndex::new(0), + name: IdentifierIndex(0), + address: AddressPoolIndex(0), }], struct_handles: vec![], + signatures: vec![Signature(vec![]), Signature(vec![SignatureToken::U64])], function_handles: vec![ FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(1), - signature: FunctionSignatureIndex::new(0), + module: ModuleHandleIndex(0), + name: IdentifierIndex(1), + return_: SignatureIndex(0), + parameters: SignatureIndex(0), + type_parameters: vec![], }, FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(2), - signature: FunctionSignatureIndex::new(1), + module: ModuleHandleIndex(0), + name: IdentifierIndex(2), + return_: SignatureIndex(0), + parameters: SignatureIndex(1), + type_parameters: vec![], }, ], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], struct_defs: vec![], - field_defs: vec![], function_defs: vec![ FunctionDefinition { - function: FunctionHandleIndex::new(0), + function: FunctionHandleIndex(0), flags: CodeUnit::PUBLIC, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 10, - locals: LocalsSignatureIndex::new(0), + locals: SignatureIndex(0), code: vec![Bytecode::LdTrue, Bytecode::Pop, Bytecode::Ret], }, }, FunctionDefinition { - function: FunctionHandleIndex::new(1), + function: FunctionHandleIndex(1), flags: CodeUnit::PUBLIC, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 10, - locals: LocalsSignatureIndex::new(1), + locals: SignatureIndex(1), code: vec![Bytecode::Ret], }, }, ], - type_signatures: vec![], - function_signatures: vec![ - FunctionSignature { - return_types: vec![], - arg_types: vec![], - type_formals: vec![], - }, - FunctionSignature { - return_types: vec![], - arg_types: vec![SignatureToken::U64], - type_formals: vec![], - }, - ], - locals_signatures: vec![ - LocalsSignature(vec![]), - LocalsSignature(vec![SignatureToken::U64]), - ], identifiers: idents(vec![name, "func1", "func2"]), byte_array_pool: vec![], address_pool: vec![AccountAddress::default()], @@ -147,57 +138,51 @@ fn test_module(name: &'static str) -> VerifiedModule { fn test_script() -> VerifiedScript { let compiled_script = CompiledScriptMut { main: FunctionDefinition { - function: FunctionHandleIndex::new(0), + function: FunctionHandleIndex(0), flags: CodeUnit::PUBLIC, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 10, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Bytecode::Ret], }, }, module_handles: vec![ ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(0), + address: AddressPoolIndex(0), + name: IdentifierIndex(0), }, ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(1), + address: AddressPoolIndex(0), + name: IdentifierIndex(1), }, ], struct_handles: vec![], + signatures: vec![Signature(vec![]), Signature(vec![SignatureToken::U64])], function_handles: vec![ FunctionHandle { - name: IdentifierIndex::new(4), - signature: FunctionSignatureIndex::new(0), - module: ModuleHandleIndex::new(0), + name: IdentifierIndex(4), + module: ModuleHandleIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }, FunctionHandle { - name: IdentifierIndex::new(2), - signature: FunctionSignatureIndex::new(0), - module: ModuleHandleIndex::new(1), + name: IdentifierIndex(2), + module: ModuleHandleIndex(1), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }, FunctionHandle { - name: IdentifierIndex::new(3), - signature: FunctionSignatureIndex::new(1), - module: ModuleHandleIndex::new(1), + name: IdentifierIndex(3), + module: ModuleHandleIndex(1), + parameters: SignatureIndex(1), + return_: SignatureIndex(0), + type_parameters: vec![], }, ], - type_signatures: vec![], - function_signatures: vec![ - FunctionSignature { - return_types: vec![], - arg_types: vec![], - type_formals: vec![], - }, - FunctionSignature { - return_types: vec![], - arg_types: vec![SignatureToken::U64], - type_formals: vec![], - }, - ], - locals_signatures: vec![LocalsSignature(vec![])], + function_instantiations: vec![], identifiers: idents(vec!["hello", "module", "func1", "func2", "main"]), byte_array_pool: vec![], address_pool: vec![AccountAddress::default()], @@ -225,10 +210,10 @@ fn test_loader_one_module() { // Get the function reference of the first two function handles. let func1_ref = loaded_program - .resolve_function_ref(module_ref, FunctionHandleIndex::new(0), &ctx) + .resolve_function_ref(module_ref, FunctionHandleIndex(0), &ctx) .unwrap(); let func2_ref = loaded_program - .resolve_function_ref(module_ref, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(module_ref, FunctionHandleIndex(1), &ctx) .unwrap(); // The two references should refer to the same module @@ -266,10 +251,10 @@ fn test_loader_cross_modules() { let entry_func = FunctionRef::new(&loaded_main, CompiledScript::MAIN_INDEX); let entry_module = entry_func.module(); let func1 = loaded_program - .resolve_function_ref(entry_module, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(1), &ctx) .unwrap(); let func2 = loaded_program - .resolve_function_ref(entry_module, FunctionHandleIndex::new(2), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(2), &ctx) .unwrap(); assert_eq!( @@ -297,7 +282,6 @@ fn test_cache_with_storage() { let loaded_main = LoadedModule::new(owned_entry_module); let entry_func = FunctionRef::new(&loaded_main, CompiledScript::MAIN_INDEX); let entry_module = entry_func.module(); - println!("MODULE: {}", entry_module.as_module()); let vm_cache = VMModuleCache::new(&allocator); @@ -306,7 +290,7 @@ fn test_cache_with_storage() { // Function is not defined locally. assert!(vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(1), &ctx) .is_err()); let mut data_cache = FakeDataCache::default(); @@ -315,10 +299,10 @@ fn test_cache_with_storage() { // Make sure the block cache fetches the code from the view. let func1 = vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(1), &ctx) .unwrap(); let func2 = vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(2), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(2), &ctx) .unwrap(); assert_eq!( @@ -342,10 +326,10 @@ fn test_cache_with_storage() { let ctx = SystemExecutionContext::new(&data_cache, GasUnits::new(0)); let func1 = vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(1), &ctx) .unwrap(); let func2 = vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(2), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(2), &ctx) .unwrap(); assert_eq!( @@ -377,67 +361,61 @@ fn test_multi_level_cache_write_back() { // Create a new script that refers to both published and unpublished modules. let script = CompiledScriptMut { main: FunctionDefinition { - function: FunctionHandleIndex::new(0), + function: FunctionHandleIndex(0), flags: CodeUnit::PUBLIC, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 10, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Bytecode::Ret], }, }, module_handles: vec![ // Self ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(0), + address: AddressPoolIndex(0), + name: IdentifierIndex(0), }, // To-be-published Module ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(1), + address: AddressPoolIndex(0), + name: IdentifierIndex(1), }, // Existing module on chain ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(2), + address: AddressPoolIndex(0), + name: IdentifierIndex(2), }, ], struct_handles: vec![], + signatures: vec![Signature(vec![]), Signature(vec![SignatureToken::U64])], function_handles: vec![ // main FunctionHandle { - name: IdentifierIndex::new(5), - signature: FunctionSignatureIndex::new(0), - module: ModuleHandleIndex::new(0), + name: IdentifierIndex(5), + module: ModuleHandleIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }, // Func2 defined in the new module FunctionHandle { - name: IdentifierIndex::new(4), - signature: FunctionSignatureIndex::new(0), - module: ModuleHandleIndex::new(1), + name: IdentifierIndex(4), + module: ModuleHandleIndex(1), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }, // Func1 defined in the old module FunctionHandle { - name: IdentifierIndex::new(3), - signature: FunctionSignatureIndex::new(1), - module: ModuleHandleIndex::new(2), + name: IdentifierIndex(3), + module: ModuleHandleIndex(2), + parameters: SignatureIndex(1), + return_: SignatureIndex(0), + type_parameters: vec![], }, ], - type_signatures: vec![], - function_signatures: vec![ - FunctionSignature { - return_types: vec![], - arg_types: vec![], - type_formals: vec![], - }, - FunctionSignature { - return_types: vec![], - arg_types: vec![SignatureToken::U64], - type_formals: vec![], - }, - ], - locals_signatures: vec![LocalsSignature(vec![])], + function_instantiations: vec![], identifiers: idents(vec![ "hello", "module", @@ -464,7 +442,7 @@ fn test_multi_level_cache_write_back() { let data_cache = FakeDataCache::default(); let ctx = SystemExecutionContext::new(&data_cache, GasUnits::new(0)); let func2_ref = vm_cache - .resolve_function_ref(entry_module, FunctionHandleIndex::new(1), &ctx) + .resolve_function_ref(entry_module, FunctionHandleIndex(1), &ctx) .unwrap(); assert_eq!(func2_ref.arg_count(), 1); assert_eq!(func2_ref.return_count(), 0); @@ -597,42 +575,6 @@ fn test_multi_module_struct_resolution() { assert_eq!(struct_t, struct_t_expected_ty,); } -#[test] -fn test_field_offset_resolution() { - let allocator = Arena::new(); - let vm_cache = VMModuleCache::new(&allocator); - - let code = " - module M1 { - struct X { f: u64, g: bool} - struct T { i: u64, x: Self.X, y: u64 } - } - "; - - let mut data_cache = FakeDataCache::default(); - let module = parse_and_compile_module(code, vec![]); - data_cache.set(module); - let ctx = SystemExecutionContext::new(&data_cache, GasUnits::new(0)); - - let module_id = ModuleId::new(AccountAddress::default(), ident("M1")); - let module_ref = vm_cache.get_loaded_module(&module_id, &ctx).unwrap(); - - let f_idx = module_ref.field_defs_table.get(&ident("f")).unwrap(); - assert_eq!(module_ref.get_field_offset(*f_idx).unwrap(), 0); - - let g_idx = module_ref.field_defs_table.get(&ident("g")).unwrap(); - assert_eq!(module_ref.get_field_offset(*g_idx).unwrap(), 1); - - let i_idx = module_ref.field_defs_table.get(&ident("i")).unwrap(); - assert_eq!(module_ref.get_field_offset(*i_idx).unwrap(), 0); - - let x_idx = module_ref.field_defs_table.get(&ident("x")).unwrap(); - assert_eq!(module_ref.get_field_offset(*x_idx).unwrap(), 1); - - let y_idx = module_ref.field_defs_table.get(&ident("y")).unwrap(); - assert_eq!(module_ref.get_field_offset(*y_idx).unwrap(), 2); -} - #[test] fn test_dependency_fails_verification() { let allocator = Arena::new(); diff --git a/language/move-vm/types/src/native_functions/dispatch.rs b/language/move-vm/types/src/native_functions/dispatch.rs index dd9dae1904cd..5578c76e2e68 100644 --- a/language/move-vm/types/src/native_functions/dispatch.rs +++ b/language/move-vm/types/src/native_functions/dispatch.rs @@ -19,7 +19,7 @@ use std::collections::VecDeque; use vm::{ access::ModuleAccess, errors::VMResult, - file_format::{FunctionSignature, Kind, SignatureToken, StructHandleIndex}, + file_format::{FunctionSignature, Kind, Signature, SignatureToken, StructHandleIndex}, gas_schedule::{ AbstractMemorySize, CostTable, GasAlgebra, GasCarrier, GasUnits, NativeCostIndex, }, @@ -164,6 +164,27 @@ impl NativeFunction { } } + pub fn parameters( + self, + m: Option<&ModuleView>, + ) -> VMResult> { + Ok(self.signature(m)?.map(|res| Signature(res.parameters))) + } + + pub fn return_( + self, + m: Option<&ModuleView>, + ) -> VMResult> { + Ok(self.signature(m)?.map(|res| Signature(res.return_))) + } + + pub fn type_parameters( + self, + m: Option<&ModuleView>, + ) -> VMResult>> { + Ok(self.signature(m)?.map(|res| res.type_parameters)) + } + /// The signature as defined in it's declaring module. /// It should NOT be generally inspected outside of it's declaring module as the various /// struct handle indexes are not remapped into the local context. @@ -182,7 +203,7 @@ impl NativeFunction { None => return Ok(None), Some(res) => res, }; - if self.num_args() == res.arg_types.len() { + if self.num_args() == res.parameters.len() { Ok(Some(res)) } else { Err( @@ -203,9 +224,9 @@ impl NativeFunction { }}; ($kinds:expr, $args:expr, $ret:expr) => {{ FunctionSignature { - return_types: $ret, - arg_types: $args, - type_formals: $kinds, + return_: $ret, + parameters: $args, + type_parameters: $kinds, } }}; } @@ -284,12 +305,12 @@ impl NativeFunction { vec![] ), Self::AccountWriteEvent => simple!( - vec![Kind::Unrestricted], + vec![Kind::Copyable], vec![Vector(Box::new(U8)), U64, TypeParameter(0)], vec![] ), Self::AccountSaveAccount => { - let type_formals = vec![Kind::All]; + let type_parameters = vec![Kind::All]; let self_t_idx = struct_handle_idx( m?, &CORE_CODE_ADDRESS, @@ -302,16 +323,16 @@ impl NativeFunction { account_module_name().as_str(), account_balance_struct_name().as_str(), )?; - let arg_types = vec![ - Struct(balance_t_idx, vec![TypeParameter(0)]), - Struct(self_t_idx, vec![]), + let parameters = vec![ + StructInstantiation(balance_t_idx, vec![TypeParameter(0)]), + Struct(self_t_idx), Address, ]; - let return_types = vec![]; + let return_ = vec![]; FunctionSignature { - type_formals, - arg_types, - return_types, + type_parameters, + parameters, + return_, } } }) @@ -330,7 +351,7 @@ fn struct_handle_idx( && handle.module_id().name().as_str() == module_name && handle.module_id().address() == module_address { - Some(StructHandleIndex::new(idx as u16)) + Some(StructHandleIndex(idx as u16)) } else { None } diff --git a/language/stdlib/staged/stdlib.mv b/language/stdlib/staged/stdlib.mv index 9d4a55846b5f..0c8f71f745ac 100644 Binary files a/language/stdlib/staged/stdlib.mv and b/language/stdlib/staged/stdlib.mv differ diff --git a/language/stdlib/staged/transaction_scripts/add_validator.mv b/language/stdlib/staged/transaction_scripts/add_validator.mv index be4c5f105c5d..d346ef1e9e13 100644 Binary files a/language/stdlib/staged/transaction_scripts/add_validator.mv and b/language/stdlib/staged/transaction_scripts/add_validator.mv differ diff --git a/language/stdlib/staged/transaction_scripts/approved_payment.mv b/language/stdlib/staged/transaction_scripts/approved_payment.mv index c40123940fd8..f6c598fa0242 100644 Binary files a/language/stdlib/staged/transaction_scripts/approved_payment.mv and b/language/stdlib/staged/transaction_scripts/approved_payment.mv differ diff --git a/language/stdlib/staged/transaction_scripts/burn.mv b/language/stdlib/staged/transaction_scripts/burn.mv index cdaadeadf6fb..0054f12fc1db 100644 Binary files a/language/stdlib/staged/transaction_scripts/burn.mv and b/language/stdlib/staged/transaction_scripts/burn.mv differ diff --git a/language/stdlib/staged/transaction_scripts/cancel_burn.mv b/language/stdlib/staged/transaction_scripts/cancel_burn.mv index 4ea41a351a72..3c6913c1e8e0 100644 Binary files a/language/stdlib/staged/transaction_scripts/cancel_burn.mv and b/language/stdlib/staged/transaction_scripts/cancel_burn.mv differ diff --git a/language/stdlib/staged/transaction_scripts/create_account.mv b/language/stdlib/staged/transaction_scripts/create_account.mv index bb5ae515de8d..f707b446ebf5 100644 Binary files a/language/stdlib/staged/transaction_scripts/create_account.mv and b/language/stdlib/staged/transaction_scripts/create_account.mv differ diff --git a/language/stdlib/staged/transaction_scripts/empty_script.mv b/language/stdlib/staged/transaction_scripts/empty_script.mv index 1639586af88e..96e61e8a0a3d 100644 Binary files a/language/stdlib/staged/transaction_scripts/empty_script.mv and b/language/stdlib/staged/transaction_scripts/empty_script.mv differ diff --git a/language/stdlib/staged/transaction_scripts/mint.mv b/language/stdlib/staged/transaction_scripts/mint.mv index adbadf169220..089e60695f4c 100644 Binary files a/language/stdlib/staged/transaction_scripts/mint.mv and b/language/stdlib/staged/transaction_scripts/mint.mv differ diff --git a/language/stdlib/staged/transaction_scripts/modify_publishing_option.mv b/language/stdlib/staged/transaction_scripts/modify_publishing_option.mv index 2cb971b2938e..7ed59e90d6d4 100644 Binary files a/language/stdlib/staged/transaction_scripts/modify_publishing_option.mv and b/language/stdlib/staged/transaction_scripts/modify_publishing_option.mv differ diff --git a/language/stdlib/staged/transaction_scripts/peer_to_peer.mv b/language/stdlib/staged/transaction_scripts/peer_to_peer.mv index cc9b214f1f50..90eae759bd06 100644 Binary files a/language/stdlib/staged/transaction_scripts/peer_to_peer.mv and b/language/stdlib/staged/transaction_scripts/peer_to_peer.mv differ diff --git a/language/stdlib/staged/transaction_scripts/peer_to_peer_with_metadata.mv b/language/stdlib/staged/transaction_scripts/peer_to_peer_with_metadata.mv index b1f7cf5f50e5..79d16bc02f9d 100644 Binary files a/language/stdlib/staged/transaction_scripts/peer_to_peer_with_metadata.mv and b/language/stdlib/staged/transaction_scripts/peer_to_peer_with_metadata.mv differ diff --git a/language/stdlib/staged/transaction_scripts/preburn.mv b/language/stdlib/staged/transaction_scripts/preburn.mv index 07ce2de684f4..2232bf1ba0f1 100644 Binary files a/language/stdlib/staged/transaction_scripts/preburn.mv and b/language/stdlib/staged/transaction_scripts/preburn.mv differ diff --git a/language/stdlib/staged/transaction_scripts/register_approved_payment.mv b/language/stdlib/staged/transaction_scripts/register_approved_payment.mv index c8d6f9468de4..e9ac7b566e16 100644 Binary files a/language/stdlib/staged/transaction_scripts/register_approved_payment.mv and b/language/stdlib/staged/transaction_scripts/register_approved_payment.mv differ diff --git a/language/stdlib/staged/transaction_scripts/register_preburner.mv b/language/stdlib/staged/transaction_scripts/register_preburner.mv index 8d44cd6c9e02..ffd74156f22d 100644 Binary files a/language/stdlib/staged/transaction_scripts/register_preburner.mv and b/language/stdlib/staged/transaction_scripts/register_preburner.mv differ diff --git a/language/stdlib/staged/transaction_scripts/register_validator.mv b/language/stdlib/staged/transaction_scripts/register_validator.mv index a4be2e8d25de..92651a6e2a5c 100644 Binary files a/language/stdlib/staged/transaction_scripts/register_validator.mv and b/language/stdlib/staged/transaction_scripts/register_validator.mv differ diff --git a/language/stdlib/staged/transaction_scripts/remove_validator.mv b/language/stdlib/staged/transaction_scripts/remove_validator.mv index 9a0649479732..35a20184bd7b 100644 Binary files a/language/stdlib/staged/transaction_scripts/remove_validator.mv and b/language/stdlib/staged/transaction_scripts/remove_validator.mv differ diff --git a/language/stdlib/staged/transaction_scripts/rotate_authentication_key.mv b/language/stdlib/staged/transaction_scripts/rotate_authentication_key.mv index 6b28233e6871..2b5746b515db 100644 Binary files a/language/stdlib/staged/transaction_scripts/rotate_authentication_key.mv and b/language/stdlib/staged/transaction_scripts/rotate_authentication_key.mv differ diff --git a/language/stdlib/staged/transaction_scripts/rotate_consensus_pubkey.mv b/language/stdlib/staged/transaction_scripts/rotate_consensus_pubkey.mv index 970c23968831..c9e28d028b13 100644 Binary files a/language/stdlib/staged/transaction_scripts/rotate_consensus_pubkey.mv and b/language/stdlib/staged/transaction_scripts/rotate_consensus_pubkey.mv differ diff --git a/language/tools/cost-synthesis/src/bytecode_specifications/stack_transition_info.rs b/language/tools/cost-synthesis/src/bytecode_specifications/stack_transition_info.rs index fcb80bcf764a..920b2d515eda 100644 --- a/language/tools/cost-synthesis/src/bytecode_specifications/stack_transition_info.rs +++ b/language/tools/cost-synthesis/src/bytecode_specifications/stack_transition_info.rs @@ -74,7 +74,7 @@ static BASE_SIG_TOKENS: Lazy> = Lazy::new(|| { SignatureToken::Address, // Bogus struct handle index, but it's fine since we disregard this in the generation of // instruction arguments. - SignatureToken::Struct(StructHandleIndex::new(0), vec![]), + SignatureToken::Struct(StructHandleIndex(0)), ] }); @@ -116,7 +116,7 @@ fn ref_values(num: u64) -> Vec { fn ref_resources(num: u64) -> Vec { (0..num) - .map(|_| simple_ref_of_sig_tok(SignatureToken::Struct(StructHandleIndex::new(0), vec![]))) + .map(|_| simple_ref_of_sig_tok(SignatureToken::Struct(StructHandleIndex(0)))) .collect() } @@ -180,7 +180,7 @@ fn integer_values(num: u64) -> Vec> { fn resources(num: u64) -> Vec { (0..num) - .map(|_| ty_of_sig_tok(SignatureToken::Struct(StructHandleIndex::new(0), vec![]))) + .map(|_| ty_of_sig_tok(SignatureToken::Struct(StructHandleIndex(0)))) .collect() } @@ -265,7 +265,9 @@ pub fn call_details(op: &Bytecode) -> Vec { Bytecode::MutBorrowLoc(_) | Bytecode::ImmBorrowLoc(_) | Bytecode::ImmBorrowField(_) - | Bytecode::MutBorrowField(_) => { + | Bytecode::ImmBorrowFieldGeneric(_) + | Bytecode::MutBorrowField(_) + | Bytecode::MutBorrowFieldGeneric(_) => { type_transition! { empty() => ref_values(1), empty() => ref_resources(1) } } Bytecode::ReadRef => type_transition! { ref_values(1) => values(1) }, @@ -287,7 +289,10 @@ pub fn call_details(op: &Bytecode) -> Vec { ref_values(1) => empty(), ref_resources(1) => empty() }, - Bytecode::Pack(_, _) | Bytecode::Call(_, _) => { + Bytecode::Pack(_) + | Bytecode::Call(_) + | Bytecode::PackGeneric(_) + | Bytecode::CallGeneric(_) => { let possible_tys = BASE_SIG_TOKENS.clone(); type_transition! { vec![variable_ty_of_sig_tok( @@ -296,7 +301,7 @@ pub fn call_details(op: &Bytecode) -> Vec { )] => vec![variable_ty_of_sig_tok(possible_tys, 1)] } } - Bytecode::Unpack(_, _) => { + Bytecode::Unpack(_) | Bytecode::UnpackGeneric(_) => { let possible_tys = BASE_SIG_TOKENS.clone(); type_transition! { vec![variable_ty_of_sig_tok( @@ -310,12 +315,21 @@ pub fn call_details(op: &Bytecode) -> Vec { | Bytecode::GetTxnMaxGasUnits | Bytecode::GetGasRemaining => type_transition! { empty() => u64s(1) }, Bytecode::GetTxnSenderAddress => type_transition! { empty() => simple_addrs(1) }, - Bytecode::Exists(_, _) => type_transition! { simple_addrs(1) => bools(1) }, - Bytecode::MutBorrowGlobal(_, _) | Bytecode::ImmBorrowGlobal(_, _) => { + Bytecode::Exists(_) | Bytecode::ExistsGeneric(_) => { + type_transition! { simple_addrs(1) => bools(1) } + } + Bytecode::MutBorrowGlobal(_) + | Bytecode::ImmBorrowGlobal(_) + | Bytecode::MutBorrowGlobalGeneric(_) + | Bytecode::ImmBorrowGlobalGeneric(_) => { type_transition! { simple_addrs(1) => ref_values(1) } } - Bytecode::MoveFrom(_, _) => type_transition! { simple_addrs(1) => values(1) }, - Bytecode::MoveToSender(_, _) => type_transition! { values(1) => empty() }, + Bytecode::MoveFrom(_) | Bytecode::MoveFromGeneric(_) => { + type_transition! { simple_addrs(1) => values(1) } + } + Bytecode::MoveToSender(_) | Bytecode::MoveToSenderGeneric(_) => { + type_transition! { values(1) => empty() } + } Bytecode::GetTxnPublicKey => type_transition! { empty() => byte_arrays(1) }, Bytecode::FreezeRef => type_transition! { ref_values(1) => ref_values(1) }, } diff --git a/language/tools/cost-synthesis/src/global_state/account.rs b/language/tools/cost-synthesis/src/global_state/account.rs index 21f2d1541b72..d8c75b39b091 100644 --- a/language/tools/cost-synthesis/src/global_state/account.rs +++ b/language/tools/cost-synthesis/src/global_state/account.rs @@ -66,7 +66,7 @@ impl Account { .is_nominal_resource; if is_nominal_resource { // Generate the type for the struct - let typ = SignatureToken::Struct(struct_def.struct_handle, vec![]); + let typ = SignatureToken::Struct(struct_def.struct_handle); // Generate a value of that type let (ty, struct_val) = inhabitor.inhabit(&typ); // Now serialize that value into the correct binary blob. diff --git a/language/tools/cost-synthesis/src/global_state/inhabitor.rs b/language/tools/cost-synthesis/src/global_state/inhabitor.rs index 271e19f31206..63023a1c156a 100644 --- a/language/tools/cost-synthesis/src/global_state/inhabitor.rs +++ b/language/tools/cost-synthesis/src/global_state/inhabitor.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; use vm::{ access::*, file_format::{ - MemberCount, ModuleHandle, SignatureToken, StructDefinition, StructDefinitionIndex, + ModuleHandle, SignatureToken, StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandleIndex, TableIndex, }, }; @@ -143,28 +143,21 @@ impl<'txn> RandomInhabitor<'txn> { SignatureToken::Reference(_sig) | SignatureToken::MutableReference(_sig) => { panic!("cannot inhabit references"); } - SignatureToken::Struct(struct_handle_idx, _) => { + SignatureToken::Struct(struct_handle_idx) + | SignatureToken::StructInstantiation(struct_handle_idx, _) => { assert!(self.root_module.struct_defs().len() > 1); let struct_definition = self .root_module .struct_def_at(self.resolve_struct_handle(*struct_handle_idx).2); - let (num_fields, index) = match struct_definition.field_information { + let fields = match &struct_definition.field_information { StructFieldInformation::Native => { panic!("[Struct Generation] Unexpected native struct") } - StructFieldInformation::Declared { - field_count, - fields, - } => (field_count as usize, fields), + StructFieldInformation::Declared(fields) => fields, }; - let fields = self - .root_module - .field_def_range(num_fields as MemberCount, index); let (layouts, values): (Vec<_>, Vec<_>) = fields .iter() - .map(|field| { - self.inhabit(&self.root_module.type_signature_at(field.signature).0) - }) + .map(|field| self.inhabit(&field.signature.0)) .unzip(); ( Type::Struct(Box::new(StructType { diff --git a/language/tools/cost-synthesis/src/main.rs b/language/tools/cost-synthesis/src/main.rs index a76bdeb3ebf6..93748bd61f04 100644 --- a/language/tools/cost-synthesis/src/main.rs +++ b/language/tools/cost-synthesis/src/main.rs @@ -35,8 +35,8 @@ use structopt::StructOpt; use vm::{ access::ModuleAccess, file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldDefinitionIndex, - FunctionDefinitionIndex, FunctionHandleIndex, StructDefinitionIndex, NO_TYPE_ACTUALS, + AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldHandleIndex, FunctionDefinitionIndex, + FunctionHandleIndex, StructDefinitionIndex, }, gas_schedule::{AbstractMemorySize, CostTable, GasAlgebra, GasCarrier, GasUnits}, transaction_metadata::TransactionMetadata, @@ -77,22 +77,20 @@ fn output_to_csv(path: &Path, data: HashMap>, output: bool) { fn size_normalize_cost(instr: &Bytecode, cost: u64, size: AbstractMemorySize) -> u64 { match instr { - Bytecode::MoveToSender(_, _) - | Bytecode::Exists(_, _) - | Bytecode::MutBorrowGlobal(_, _) - | Bytecode::ImmBorrowGlobal(_, _) + Bytecode::MoveToSender(_) + | Bytecode::Exists(_) + | Bytecode::MutBorrowGlobal(_) + | Bytecode::ImmBorrowGlobal(_) | Bytecode::Eq | Bytecode::Neq | Bytecode::LdByteArray(_) | Bytecode::StLoc(_) | Bytecode::CopyLoc(_) - | Bytecode::Pack(_, _) - | Bytecode::Unpack(_, _) + | Bytecode::Pack(_) + | Bytecode::Unpack(_) | Bytecode::WriteRef | Bytecode::ReadRef - | Bytecode::MoveFrom(_, _) => { - cost / size.get() + if cost % size.get() == 0 { 0 } else { 1 } - } + | Bytecode::MoveFrom(_) => cost / size.get() + if cost % size.get() == 0 { 0 } else { 1 }, _ => cost, } } @@ -103,21 +101,21 @@ fn stack_instructions(options: &Opt) { ReadRef, WriteRef, FreezeRef, - MoveToSender(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - Exists(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - MutBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - ImmBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - MoveFrom(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - MutBorrowField(FieldDefinitionIndex::new(0)), - ImmBorrowField(FieldDefinitionIndex::new(0)), + MoveToSender(StructDefinitionIndex(0)), + Exists(StructDefinitionIndex(0)), + MutBorrowGlobal(StructDefinitionIndex(0)), + ImmBorrowGlobal(StructDefinitionIndex(0)), + MoveFrom(StructDefinitionIndex(0)), + MutBorrowField(FieldHandleIndex(0)), + ImmBorrowField(FieldHandleIndex(0)), CopyLoc(0), MoveLoc(0), MutBorrowLoc(0), ImmBorrowLoc(0), StLoc(0), - Unpack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - Pack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), - Call(FunctionHandleIndex::new(0), NO_TYPE_ACTUALS), + Unpack(StructDefinitionIndex(0)), + Pack(StructDefinitionIndex(0)), + Call(FunctionHandleIndex(0)), Sub, Ret, Add, @@ -142,8 +140,8 @@ fn stack_instructions(options: &Opt) { LdFalse, LdTrue, LdU64(0), - LdByteArray(ByteArrayPoolIndex::new(0)), - LdAddr(AddressPoolIndex::new(0)), + LdByteArray(ByteArrayPoolIndex(0)), + LdAddr(AddressPoolIndex(0)), BrFalse(0), BrTrue(0), Branch(0), @@ -194,7 +192,7 @@ fn stack_instructions(options: &Opt) { let mut vm = InterpreterForCostSynthesis::new(&txn_data, &gas_schedule); // load the entry point - let entry_idx = FunctionDefinitionIndex::new(0); + let entry_idx = FunctionDefinitionIndex(0); let entry_func = FunctionRef::new(&loaded_module, entry_idx); vm.push_frame(entry_func, vec![]); diff --git a/language/tools/cost-synthesis/src/stack_generator.rs b/language/tools/cost-synthesis/src/stack_generator.rs index 811694ba08f9..54090feabfee 100644 --- a/language/tools/cost-synthesis/src/stack_generator.rs +++ b/language/tools/cost-synthesis/src/stack_generator.rs @@ -22,11 +22,10 @@ use vm::{ access::*, errors::VMResult, file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, FieldDefinitionIndex, - FunctionDefinition, FunctionDefinitionIndex, FunctionHandleIndex, FunctionSignature, - LocalIndex, MemberCount, ModuleHandle, SignatureToken, StructDefinition, - StructDefinitionIndex, StructFieldInformation, StructHandleIndex, TableIndex, - NO_TYPE_ACTUALS, + AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, FieldHandleIndex, + FunctionDefinition, FunctionDefinitionIndex, FunctionHandle, FunctionHandleIndex, + LocalIndex, ModuleHandle, SignatureToken, StructDefinition, StructDefinitionIndex, + StructFieldInformation, StructHandleIndex, TableIndex, }, gas_schedule::{AbstractMemorySize, GasAlgebra, GasCarrier}, }; @@ -168,21 +167,31 @@ impl<'txn> RandomStackGenerator<'txn> { fn is_module_specific_op(&self) -> bool { use Bytecode::*; matches!(self.op, - MoveToSender(_, _) | - MoveFrom(_, _) | - ImmBorrowGlobal(_, _) | - MutBorrowGlobal(_, _) | - Exists(_, _) | - Unpack(_, _) | - Pack(_, _) | - Call(_, _) | + MoveToSender(_) | + MoveToSenderGeneric(_) | + MoveFrom(_) | + MoveFromGeneric(_) | + ImmBorrowGlobal(_) | + ImmBorrowGlobalGeneric(_) | + MutBorrowGlobal(_) | + MutBorrowGlobalGeneric(_) | + Exists(_) | + ExistsGeneric(_) | + Unpack(_) | + UnpackGeneric(_) | + Pack(_) | + PackGeneric(_) | + Call(_) | + CallGeneric(_) | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | ImmBorrowField(_) | - MutBorrowField(_) + ImmBorrowFieldGeneric(_) | + MutBorrowField(_) | + MutBorrowFieldGeneric(_) ) } @@ -320,17 +329,15 @@ impl<'txn> RandomStackGenerator<'txn> { ) { // We pick a random function from the module in which to store the local let function_handle_idx = self.next_function_handle_idx(); - let (module, function_definition, function_def_idx, function_sig) = + let (module, function_definition, function_def_idx, function_handle) = self.resolve_function_handle(function_handle_idx); - let type_sig = &module - .locals_signature_at(function_definition.code.locals) - .0; + let type_sig = &module.signature_at(function_definition.code.locals).0; // Pick a random local within that function in which we'll store the local let local_index = self.gen.gen_range(0, type_sig.len()); let type_tok = &type_sig[local_index]; let stack_local = self.resolve_to_value(type_tok, &[]); let mut stack = vec![stack_local]; - for type_tok in function_sig.arg_types.iter() { + for type_tok in module.signature_at(function_handle.parameters).0.iter() { stack.push(self.resolve_to_value(type_tok, &[])) } (module, local_index as LocalIndex, function_def_idx, stack) @@ -436,12 +443,9 @@ impl<'txn> RandomStackGenerator<'txn> { &'txn LoadedModule, &'txn FunctionDefinition, FunctionDefinitionIndex, - &'txn FunctionSignature, + &'txn FunctionHandle, ) { let function_handle = self.root_module.function_handle_at(function_handle_index); - let function_signature = self - .root_module - .function_signature_at(function_handle.signature); let function_name = self.root_module.identifier_at(function_handle.name); let module_handle = self.root_module.module_handle_at(function_handle.module); let module_id = self.to_module_id(module_handle); @@ -471,7 +475,7 @@ impl<'txn> RandomStackGenerator<'txn> { .unwrap(); let function_def = module.function_def_at(function_def_idx); - (module, function_def, function_def_idx, function_signature) + (module, function_def, function_def_idx, function_handle) } // Build an inhabitant of the type given by `sig_token`. We pass the current stack state in @@ -493,29 +497,21 @@ impl<'txn> RandomStackGenerator<'txn> { .unwrap(); locals.borrow_loc(0).unwrap() } - SignatureToken::Struct(struct_handle_idx, _) => { + SignatureToken::StructInstantiation(struct_handle_idx, _) + | SignatureToken::Struct(struct_handle_idx) => { assert!(self.root_module.struct_defs().len() > 1); let struct_definition = self .root_module .struct_def_at(self.resolve_struct_handle(*struct_handle_idx).2); - let (num_fields, index) = match struct_definition.field_information { + let fields = match &struct_definition.field_information { StructFieldInformation::Native => { panic!("[Struct Generation] Unexpected native struct") } - StructFieldInformation::Declared { - field_count, - fields, - } => (field_count, fields), + StructFieldInformation::Declared(fields) => fields, }; - let fields = self - .root_module - .field_def_range(num_fields as MemberCount, index); - let values = fields.iter().map(|field| { - self.resolve_to_value( - &self.root_module.type_signature_at(field.signature).0, - stk, - ) - }); + let values = fields + .iter() + .map(|field| self.resolve_to_value(&field.signature.0, stk)); Value::struct_(Struct::pack(values)) } SignatureToken::Vector(_) => unimplemented!(), @@ -547,14 +543,14 @@ impl<'txn> RandomStackGenerator<'txn> { fn generate_from_module_info(&mut self) -> StackState<'txn> { use Bytecode::*; match &self.op { - MoveToSender(_, _) => { + MoveToSender(_) => { let struct_handle_idx = self.next_resource(); // We can just pick a random address -- this is incorrect by the bytecode semantics // (since we're moving to an account that doesn't exist), but since we don't need // correctness beyond this instruction it's OK. let struct_definition = self.root_module.struct_def_at(struct_handle_idx); let struct_stack = self.resolve_to_value( - &SignatureToken::Struct(struct_definition.struct_handle, vec![]), + &SignatureToken::Struct(struct_definition.struct_handle), &[], ); let size = struct_stack.size(); @@ -562,12 +558,12 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - MoveToSender(struct_handle_idx, NO_TYPE_ACTUALS), + MoveToSender(struct_handle_idx), size, HashMap::new(), ) } - MoveFrom(_, _) => { + MoveFrom(_) => { let struct_handle_idx = self.next_resource(); let addr = Value::address(*self.account_address); let size = addr.size(); @@ -575,12 +571,12 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - MoveFrom(struct_handle_idx, NO_TYPE_ACTUALS), + MoveFrom(struct_handle_idx), size, HashMap::new(), ) } - MutBorrowGlobal(_, _) => { + MutBorrowGlobal(_) => { let struct_handle_idx = self.next_resource(); let addr = Value::address(*self.account_address); let size = addr.size(); @@ -588,12 +584,12 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - MutBorrowGlobal(struct_handle_idx, NO_TYPE_ACTUALS), + MutBorrowGlobal(struct_handle_idx), size, HashMap::new(), ) } - ImmBorrowGlobal(_, _) => { + ImmBorrowGlobal(_) => { let struct_handle_idx = self.next_resource(); let addr = Value::address(*self.account_address); let size = addr.size(); @@ -601,12 +597,12 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - ImmBorrowGlobal(struct_handle_idx, NO_TYPE_ACTUALS), + ImmBorrowGlobal(struct_handle_idx), size, HashMap::new(), ) } - Exists(_, _) => { + Exists(_) => { let next_struct_handle_idx = self.next_resource(); // Flip a coin to determine if the resource should exist or not. let addr = if self.next_bool() { @@ -619,19 +615,18 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - Exists(next_struct_handle_idx, NO_TYPE_ACTUALS), + Exists(next_struct_handle_idx), size, HashMap::new(), ) } - Call(_, _) => { + Call(_) => { let function_handle_idx = self.next_function_handle_idx(); let function_handle = self.root_module.function_handle_at(function_handle_idx); - let function_sig = self + let stack = self .root_module - .function_signature_at(function_handle.signature); - let stack = function_sig - .arg_types + .signature_at(function_handle.parameters) + .0 .iter() .fold(Vec::new(), |mut acc, sig_tok| { acc.push(self.resolve_to_value(sig_tok, &acc)); @@ -643,36 +638,26 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - Call(function_handle_idx, NO_TYPE_ACTUALS), + Call(function_handle_idx), size, HashMap::new(), ) } - Pack(_struct_def_idx, _) => { + Pack(_struct_def_idx) => { let struct_def_bound = self.root_module.struct_defs().len() as TableIndex; let random_struct_idx = StructDefinitionIndex::new(self.next_bounded_index(struct_def_bound)); let struct_definition = self.root_module.struct_def_at(random_struct_idx); - let (num_fields, index) = match struct_definition.field_information { + let fields = match &struct_definition.field_information { StructFieldInformation::Native => { panic!("[Struct Pack] Unexpected native struct") } - StructFieldInformation::Declared { - field_count, - fields, - } => (field_count as usize, fields), + StructFieldInformation::Declared(fields) => fields, }; - let fields = self - .root_module - .field_def_range(num_fields as MemberCount, index); let stack: Stack = fields .iter() .map(|field| { - let ty = self - .root_module - .type_signature_at(field.signature) - .0 - .clone(); + let ty = field.signature.0.clone(); self.resolve_to_value(&ty, &[]) }) .collect(); @@ -682,12 +667,12 @@ impl<'txn> RandomStackGenerator<'txn> { StackState::new( (self.root_module, None), self.random_pad(stack), - Pack(random_struct_idx, NO_TYPE_ACTUALS), + Pack(random_struct_idx), size, HashMap::new(), ) } - Unpack(_struct_def_idx, _) => { + Unpack(_struct_def_idx) => { let struct_def_bound = self.root_module.struct_defs().len() as TableIndex; let random_struct_idx = StructDefinitionIndex::new(self.next_bounded_index(struct_def_bound)); @@ -696,12 +681,12 @@ impl<'txn> RandomStackGenerator<'txn> { .struct_def_at(random_struct_idx) .struct_handle; let struct_stack = - self.resolve_to_value(&SignatureToken::Struct(struct_handle_idx, vec![]), &[]); + self.resolve_to_value(&SignatureToken::Struct(struct_handle_idx), &[]); let size = struct_stack.size(); StackState::new( (self.root_module, None), self.random_pad(vec![struct_stack]), - Unpack(random_struct_idx, NO_TYPE_ACTUALS), + Unpack(random_struct_idx), size, HashMap::new(), ) @@ -718,7 +703,6 @@ impl<'txn> RandomStackGenerator<'txn> { let struct_stack = self.resolve_to_value( &SignatureToken::Reference(Box::new(SignatureToken::Struct( struct_definition.struct_handle, - vec![], ))), &[], ); @@ -729,7 +713,8 @@ impl<'txn> RandomStackGenerator<'txn> { .borrow_field(usize::from(field_index)) .expect("[BorrowField] Unable to borrow field of generated struct to get field size.") .size(); - let fdi = FieldDefinitionIndex::new(field_index); + // TODO: this is not making sense... + let fdi = FieldHandleIndex(field_index); let op = match self.op { ImmBorrowField(_) => ImmBorrowField(fdi), MutBorrowField(_) => MutBorrowField(fdi), diff --git a/language/tools/disassembler/src/disassembler.rs b/language/tools/disassembler/src/disassembler.rs index 8e1de76e24f2..644a0788cbb3 100644 --- a/language/tools/disassembler/src/disassembler.rs +++ b/language/tools/disassembler/src/disassembler.rs @@ -11,9 +11,9 @@ use move_core_types::identifier::IdentStr; use vm::{ access::ModuleAccess, file_format::{ - Bytecode, FieldDefinitionIndex, FunctionDefinition, FunctionDefinitionIndex, - FunctionSignature, Kind, LocalsSignature, LocalsSignatureIndex, SignatureToken, - StructDefinition, StructDefinitionIndex, StructFieldInformation, TableIndex, TypeSignature, + Bytecode, FieldHandleIndex, FunctionDefinition, FunctionDefinitionIndex, Kind, Signature, + SignatureToken, StructDefinition, StructDefinitionIndex, StructFieldInformation, + TableIndex, TypeSignature, }, }; @@ -93,24 +93,25 @@ impl Disassembler { // Formatting Helpers //*************************************************************************** - fn name_for_field(&self, field_idx: FieldDefinitionIndex) -> Result { - let field_def = self.source_mapper.bytecode.field_def_at(field_idx); - let struct_def_idx = self + fn name_for_field(&self, field_idx: FieldHandleIndex) -> Result { + let field_handle = self.source_mapper.bytecode.field_handle_at(field_idx); + let struct_def = self .source_mapper .bytecode - .struct_defs() - .iter() - .position(|struct_def| struct_def.struct_handle == field_def.struct_) - .ok_or_else(|| format_err!("Unable to find struct definition for struct field"))?; + .struct_def_at(field_handle.owner); + let field_def = match &struct_def.field_information { + StructFieldInformation::Native => { + return Err(format_err!("Attempt to access field on a native struct")); + } + StructFieldInformation::Declared(fields) => fields + .get(field_handle.field as usize) + .ok_or_else(|| format_err!("Bad field index"))?, + }; let field_name = self .source_mapper .bytecode .identifier_at(field_def.name) .to_string(); - let struct_def = self - .source_mapper - .bytecode - .struct_def_at(StructDefinitionIndex(struct_def_idx as TableIndex)); let struct_handle = self .source_mapper .bytecode @@ -123,42 +124,40 @@ impl Disassembler { Ok(format!("{}.{}", struct_name, field_name)) } - fn type_for_field(&self, field_idx: FieldDefinitionIndex) -> Result { - let field_def = self.source_mapper.bytecode.field_def_at(field_idx); - let struct_def_idx = self + fn type_for_field(&self, field_idx: FieldHandleIndex) -> Result { + let field_handle = self.source_mapper.bytecode.field_handle_at(field_idx); + let struct_def = self .source_mapper .bytecode - .struct_defs() - .iter() - .position(|struct_def| struct_def.struct_handle == field_def.struct_) - .ok_or_else(|| format_err!("Unable to find struct definition for struct field"))?; + .struct_def_at(field_handle.owner); + let field_def = match &struct_def.field_information { + StructFieldInformation::Native => { + return Err(format_err!("Attempt to access field on a native struct")); + } + StructFieldInformation::Declared(fields) => fields + .get(field_handle.field as usize) + .ok_or_else(|| format_err!("Bad field index"))?, + }; let struct_source_info = self .source_mapper .source_map - .get_struct_source_map(StructDefinitionIndex(struct_def_idx as TableIndex))?; - let field_type_sig = self - .source_mapper - .bytecode - .type_signature_at(field_def.signature); - let ty = self.disassemble_sig_tok( - field_type_sig.0.clone(), - &struct_source_info.type_parameters, - )?; + .get_struct_source_map(field_handle.owner)?; + let field_type_sig = field_def.signature.0.clone(); + let ty = self.disassemble_sig_tok(field_type_sig, &struct_source_info.type_parameters)?; Ok(ty) } fn struct_type_info( &self, struct_idx: StructDefinitionIndex, - types_idx: LocalsSignatureIndex, + signature: &Signature, ) -> Result<(String, String)> { let struct_definition = self.get_struct_def(struct_idx)?; let struct_source_map = self .source_mapper .source_map .get_struct_source_map(struct_idx)?; - let locals_signature = self.source_mapper.bytecode.locals_signature_at(types_idx); - let type_arguments = locals_signature + let type_arguments = signature .0 .iter() .map(|sig_tok| { @@ -197,7 +196,7 @@ impl Disassembler { fn type_for_local( &self, local_idx: u64, - locals_sigs: &LocalsSignature, + locals_sigs: &Signature, function_source_map: &FunctionSourceMap, ) -> Result { let sig_tok = locals_sigs @@ -254,7 +253,17 @@ impl Disassembler { SignatureToken::U64 => "u64".to_string(), SignatureToken::U128 => "u128".to_string(), SignatureToken::Address => "address".to_string(), - SignatureToken::Struct(struct_handle_idx, instantiation) => { + SignatureToken::Struct(struct_handle_idx) => self + .source_mapper + .bytecode + .identifier_at( + self.source_mapper + .bytecode + .struct_handle_at(struct_handle_idx) + .name, + ) + .to_string(), + SignatureToken::StructInstantiation(struct_handle_idx, instantiation) => { let instantiation = instantiation .into_iter() .map(|tok| self.disassemble_sig_tok(tok, type_param_context)) @@ -300,7 +309,7 @@ impl Disassembler { fn disassemble_instruction( &self, instruction: &Bytecode, - locals_sigs: &LocalsSignature, + locals_sigs: &Signature, function_source_map: &FunctionSourceMap, default_location: &Location, ) -> Result { @@ -352,63 +361,227 @@ impl Disassembler { let ty = self.type_for_field(*field_idx)?; Ok(format!("MutBorrowField[{}]({}: {})", field_idx, name, ty)) } + Bytecode::MutBorrowFieldGeneric(field_idx) => { + let field_inst = self + .source_mapper + .bytecode + .field_instantiation_at(*field_idx); + let name = self.name_for_field(field_inst.handle)?; + let ty = self.type_for_field(field_inst.handle)?; + Ok(format!( + "MutBorrowFieldGeneric[{}]({}: {})", + field_idx, name, ty + )) + } Bytecode::ImmBorrowField(field_idx) => { let name = self.name_for_field(*field_idx)?; let ty = self.type_for_field(*field_idx)?; Ok(format!("ImmBorrowField[{}]({}: {})", field_idx, name, ty)) } - Bytecode::Pack(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::ImmBorrowFieldGeneric(field_idx) => { + let field_inst = self + .source_mapper + .bytecode + .field_instantiation_at(*field_idx); + let name = self.name_for_field(field_inst.handle)?; + let ty = self.type_for_field(field_inst.handle)?; + Ok(format!( + "ImmBorrowFieldGeneric[{}]({}: {})", + field_idx, name, ty + )) + } + Bytecode::Pack(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!("Pack[{}]({}{})", struct_idx, name, ty_params)) } - Bytecode::Unpack(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::PackGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "PackGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::Unpack(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!("Unpack[{}]({}{})", struct_idx, name, ty_params)) } - Bytecode::Exists(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::UnpackGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "UnpackGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::Exists(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!("Exists[{}]({}{})", struct_idx, name, ty_params)) } - Bytecode::MutBorrowGlobal(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::ExistsGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "ExistsGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::MutBorrowGlobal(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!( "MutBorrowGlobal[{}]({}{})", struct_idx, name, ty_params )) } - Bytecode::ImmBorrowGlobal(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::MutBorrowGlobalGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "MutBorrowGlobalGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::ImmBorrowGlobal(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!( "ImmBorrowGlobal[{}]({}{})", struct_idx, name, ty_params )) } - Bytecode::MoveFrom(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::ImmBorrowGlobalGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "ImmBorrowGlobalGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::MoveFrom(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!("MoveFrom[{}]({}{})", struct_idx, name, ty_params)) } - Bytecode::MoveToSender(struct_idx, types_idx) => { - let (name, ty_params) = self.struct_type_info(*struct_idx, *types_idx)?; + Bytecode::MoveFromGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "MoveFromGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::MoveToSender(struct_idx) => { + let (name, ty_params) = self.struct_type_info(*struct_idx, &Signature(vec![]))?; Ok(format!( "MoveToSender[{}]({}{})", struct_idx, name, ty_params )) } - Bytecode::Call(method_idx, locals_sig_index) => { + Bytecode::MoveToSenderGeneric(struct_idx) => { + let struct_inst = self + .source_mapper + .bytecode + .struct_instantiation_at(*struct_idx); + let type_params = self + .source_mapper + .bytecode + .signature_at(struct_inst.type_parameters); + let (name, ty_params) = self.struct_type_info(struct_inst.def, type_params)?; + Ok(format!( + "MoveToSenderGeneric[{}]({}{})", + struct_idx, name, ty_params + )) + } + Bytecode::Call(method_idx) => { let function_handle = self.source_mapper.bytecode.function_handle_at(*method_idx); let fcall_name = self .source_mapper .bytecode .identifier_at(function_handle.name) .to_string(); - let function_signature = self + let type_arguments = self + .source_mapper + .bytecode + .signature_at(function_handle.parameters) + .0 + .iter() + .map(|sig_tok| Ok(self.disassemble_sig_tok(sig_tok.clone(), &[])?)) + .collect::>>()? + .join(", "); + let type_rets = self + .source_mapper + .bytecode + .signature_at(function_handle.return_) + .0 + .iter() + .map(|sig_tok| Ok(self.disassemble_sig_tok(sig_tok.clone(), &[])?)) + .collect::>>()?; + Ok(format!( + "Call[{}]({}({}){})", + method_idx, + fcall_name, + type_arguments, + Self::format_ret_type(&type_rets) + )) + } + Bytecode::CallGeneric(method_idx) => { + let func_inst = self + .source_mapper + .bytecode + .function_instantiation_at(*method_idx); + let function_handle = self .source_mapper .bytecode - .function_signature_at(function_handle.signature); + .function_handle_at(func_inst.handle); + let fcall_name = self + .source_mapper + .bytecode + .identifier_at(function_handle.name) + .to_string(); let ty_params = self .source_mapper .bytecode - .locals_signature_at(*locals_sig_index) + .signature_at(func_inst.type_parameters) .0 .iter() .map(|sig_tok| { @@ -421,14 +594,20 @@ impl Disassembler { )) }) .collect::>>()?; - let type_arguments = function_signature - .arg_types + let type_arguments = self + .source_mapper + .bytecode + .signature_at(function_handle.parameters) + .0 .iter() .map(|sig_tok| Ok(self.disassemble_sig_tok(sig_tok.clone(), &ty_params)?)) .collect::>>()? .join(", "); - let type_rets = function_signature - .return_types + let type_rets = self + .source_mapper + .bytecode + .signature_at(function_handle.return_) + .0 .iter() .map(|sig_tok| Ok(self.disassemble_sig_tok(sig_tok.clone(), &ty_params)?)) .collect::>>()?; @@ -460,7 +639,7 @@ impl Disassembler { let locals_sigs = self .source_mapper .bytecode - .locals_signature_at(function_def.code.locals); + .signature_at(function_def.code.locals); let function_source_map = self .source_mapper .source_map @@ -516,23 +695,22 @@ impl Disassembler { &self, function_source_map: &FunctionSourceMap, function_definition: &FunctionDefinition, - function_signature: &FunctionSignature, + parameters: &Signature, ) -> Result<(Vec, Vec)> { - let locals_signature = self + let signature = self .source_mapper .bytecode - .locals_signature_at(function_definition.code.locals); + .signature_at(function_definition.code.locals); let mut locals_names_tys = function_source_map .locals .iter() .enumerate() .map(|(local_idx, (name, _))| { - let ty = - self.type_for_local(local_idx as u64, locals_signature, function_source_map)?; + let ty = self.type_for_local(local_idx as u64, signature, function_source_map)?; Ok(format!("{}: {}", name.to_string(), ty)) }) .collect::>>()?; - let mut locals = locals_names_tys.split_off(function_signature.arg_types.len()); + let mut locals = locals_names_tys.split_off(parameters.len()); let args = locals_names_tys; if !self.options.print_locals { locals = vec![]; @@ -549,10 +727,6 @@ impl Disassembler { .source_mapper .bytecode .function_handle_at(function_definition.function); - let function_signature = self - .source_mapper - .bytecode - .function_signature_at(function_handle.signature); let function_source_map = self .source_mapper @@ -573,15 +747,19 @@ impl Disassembler { let ty_params = Self::disassemble_type_formals( &function_source_map.type_parameters, - &function_signature.type_formals, + &function_handle.type_parameters, ); let name = self .source_mapper .bytecode .identifier_at(function_handle.name) .to_string(); - let ret_type: Vec = function_signature - .return_types + let return_ = self + .source_mapper + .bytecode + .signature_at(function_handle.return_); + let ret_type: Vec = return_ + .0 .iter() .cloned() .map(|sig_token| { @@ -590,8 +768,12 @@ impl Disassembler { Ok(sig_tok_str) }) .collect::>>()?; + let parameters = self + .source_mapper + .bytecode + .signature_at(function_handle.parameters); let (args, locals) = - self.disassemble_locals(function_source_map, function_definition, function_signature)?; + self.disassemble_locals(function_source_map, function_definition, parameters)?; let bytecode = self.disassemble_bytecode(function_definition_index)?; Ok(format!( "{visibility_modifier}{name}{ty_params}({args}){ret_type}{body}", @@ -618,22 +800,13 @@ impl Disassembler { .get_struct_source_map(struct_def_idx)?; let field_info: Option> = - match struct_definition.field_information { + match &struct_definition.field_information { StructFieldInformation::Native => None, - StructFieldInformation::Declared { - field_count, - fields, - } => Some( - (fields.0..fields.0 + field_count) - .map(|i| { - let field_definition = self - .source_mapper - .bytecode - .field_def_at(FieldDefinitionIndex(i)); - let type_sig = self - .source_mapper - .bytecode - .type_signature_at(field_definition.signature); + StructFieldInformation::Declared(fields) => Some( + fields + .iter() + .map(|field_definition| { + let type_sig = &field_definition.signature; let field_name = self .source_mapper .bytecode @@ -660,7 +833,7 @@ impl Disassembler { let ty_params = Self::disassemble_type_formals( &struct_source_map.type_parameters, - &struct_handle.type_formals, + &struct_handle.type_parameters, ); let mut fields = match field_info { None => vec![], diff --git a/language/tools/test-generation/src/abstract_state.rs b/language/tools/test-generation/src/abstract_state.rs index 180877cea31e..045e254e24eb 100644 --- a/language/tools/test-generation/src/abstract_state.rs +++ b/language/tools/test-generation/src/abstract_state.rs @@ -9,8 +9,10 @@ use std::{ use vm::{ access::ModuleAccess, file_format::{ - empty_module, CompiledModule, CompiledModuleMut, FunctionHandleIndex, Kind, - LocalsSignature, LocalsSignatureIndex, SignatureToken, StructDefinitionIndex, TableIndex, + empty_module, CompiledModule, CompiledModuleMut, FieldInstantiation, + FieldInstantiationIndex, FunctionHandleIndex, FunctionInstantiation, + FunctionInstantiationIndex, Kind, Signature, SignatureIndex, SignatureToken, + StructDefInstantiation, StructDefInstantiationIndex, StructDefinitionIndex, TableIndex, }, }; @@ -47,11 +49,12 @@ pub enum Mutability { } impl AbstractValue { - /// Create a new primitive `AbstractValue` given its type; the kind will be `Unrestricted` + /// Create a new primitive `AbstractValue` given its type; the kind will be `Copyable` pub fn new_primitive(token: SignatureToken) -> AbstractValue { checked_precondition!( match token { - SignatureToken::Struct(_, _) => false, + SignatureToken::Struct(_) => false, + SignatureToken::StructInstantiation(_, _) => false, SignatureToken::Reference(_) => false, SignatureToken::MutableReference(_) => false, _ => true, @@ -60,7 +63,7 @@ impl AbstractValue { ); AbstractValue { token, - kind: Kind::Unrestricted, + kind: Kind::Copyable, } } @@ -76,7 +79,7 @@ impl AbstractValue { /// Create a new struct `AbstractValue` given its type and kind pub fn new_struct(token: SignatureToken, kind: Kind) -> AbstractValue { checked_precondition!( - matches!(token, SignatureToken::Struct(_, _)), + matches!(token, SignatureToken::Struct(_)), "AbstractValue::new_struct must be applied with a struct type" ); AbstractValue { token, kind } @@ -95,7 +98,7 @@ impl AbstractValue { fn is_generic_token(token: &SignatureToken) -> bool { match token { SignatureToken::TypeParameter(_) => true, - SignatureToken::Struct(_, tys) => tys.iter().any(Self::is_generic_token), + SignatureToken::StructInstantiation(_, _) => true, SignatureToken::Reference(tok) | SignatureToken::MutableReference(tok) => { Self::is_generic_token(tok) } @@ -188,12 +191,22 @@ impl CallGraph { /// 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 -/// LocalsSignatureIndex. +/// SignatureIndex. #[derive(Debug, Clone)] pub struct InstantiableModule { // A reverse lookup table for instantiations. - instance_for_offset: Vec>, - instantiations: HashMap, LocalsSignatureIndex>, + 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, } @@ -201,40 +214,128 @@ impl InstantiableModule { pub fn new(module: CompiledModule) -> Self { Self { instantiations: module - .locals_signatures() + .signatures() .iter() .enumerate() - .map(|(index, sig)| (sig.0.clone(), LocalsSignatureIndex(index as TableIndex))) + .map(|(index, sig)| (sig.0.clone(), SignatureIndex(index as TableIndex))) .collect::>(), - instance_for_offset: module - .locals_signatures() + 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 LocalsSignatureIndex for the `instantiant`. - pub fn add_instantiation(&mut self, instantiant: Vec) -> LocalsSignatureIndex { + /// 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 = - LocalsSignatureIndex(self.instance_for_offset.len() as TableIndex); + SignatureIndex(self.sig_instance_for_offset.len() as TableIndex); self.instantiations .insert(instantiant.clone(), current_index); - self.instance_for_offset.push(instantiant); + 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: LocalsSignatureIndex) -> &Vec { - match self.instance_for_offset.get(index.0 as usize) { + 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); @@ -242,15 +343,57 @@ impl InstantiableModule { } } + /// 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) -> CompiledModuleMut { let mut module = self.module.into_inner(); - module.locals_signatures = self - .instance_for_offset + module.signatures = self + .sig_instance_for_offset .into_iter() - .map(LocalsSignature) + .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 } } diff --git a/language/tools/test-generation/src/bytecode_generator.rs b/language/tools/test-generation/src/bytecode_generator.rs index 0e42d44ef96d..3b4987589a1f 100644 --- a/language/tools/test-generation/src/bytecode_generator.rs +++ b/language/tools/test-generation/src/bytecode_generator.rs @@ -8,7 +8,7 @@ use crate::{ NEGATE_PRECONDITIONS, NEGATION_PROBABILITY, VALUE_STACK_LIMIT, }, control_flow_graph::CFG, - summaries, + substitute, summaries, }; use libra_logger::{debug, error, warn}; use rand::{rngs::StdRng, Rng}; @@ -16,9 +16,10 @@ use vm::{ access::ModuleAccess, file_format::{ AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, CompiledModuleMut, - FieldDefinitionIndex, FunctionHandleIndex, FunctionSignature, LocalIndex, - LocalsSignatureIndex, SignatureToken, StructDefinitionIndex, StructFieldInformation, - TableIndex, + FieldHandleIndex, FieldInstantiationIndex, FunctionHandle, FunctionHandleIndex, + FunctionInstantiation, FunctionInstantiationIndex, LocalIndex, SignatureToken, + StructDefInstantiation, StructDefInstantiationIndex, StructDefinitionIndex, + StructFieldInformation, TableIndex, }, }; @@ -44,15 +45,22 @@ type AddressPoolIndexToBytecode = fn(AddressPoolIndex) -> Bytecode; type ByteArrayPoolIndexToBytecode = fn(ByteArrayPoolIndex) -> Bytecode; /// This type represents bytecode instructions that take a `StructDefinitionIndex` -/// and a `LocalsSignatureIndex` -type StructAndLocalIndexToBytecode = fn(StructDefinitionIndex, LocalsSignatureIndex) -> Bytecode; +type StructIndexToBytecode = fn(StructDefinitionIndex) -> Bytecode; -/// This type represents bytecode instructions that take a `FieldDefinitionIndex`` -type FieldDefinitionIndexToBytecode = fn(FieldDefinitionIndex) -> Bytecode; +/// This type represents bytecode instructions that take a `FieldHandleIndex` +type FieldHandleIndexToBytecode = fn(FieldHandleIndex) -> Bytecode; /// This type represents bytecode instructions that take a `FunctionHandleIndex` -/// and a `LocalsSignatureIndex` -type FunctionAndLocalIndexToBytecode = fn(FunctionHandleIndex, LocalsSignatureIndex) -> Bytecode; +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)] @@ -81,14 +89,23 @@ enum BytecodeType { /// Instructions that take a `ByteArrayPoolIndex` ByteArrayPoolIndex(ByteArrayPoolIndexToBytecode), - /// Instructions that take a `StructDefinitionIndex` and a `LocalsSignatureIndex` - StructAndLocalIndex(StructAndLocalIndexToBytecode), + /// 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 `FieldDefinitionIndex` - FieldDefinitionIndex(FieldDefinitionIndexToBytecode), + /// Instructions that take a `StructInstantiationIndex` + StructInstantiationIndex(StructInstIndexToBytecode), - /// Instructions that take a `FunctionHandleIndex` and a `LocalsSignatureIndex` - FunctionAndLocalIndex(FunctionAndLocalIndexToBytecode), + /// Instructions that take a `FieldInstantiationIndex` + FieldInstantiationIndex(FieldInstIndexToBytecode), + + /// Instructions that take a `FunctionInstantiationIndex` + FunctionInstantiationIndex(FunctionInstIndexToBytecode), } /// Abstraction for change to the stack size @@ -226,45 +243,82 @@ impl<'a> BytecodeGenerator<'a> { StackEffect::Add, BytecodeType::NoArg(Bytecode::GetTxnPublicKey), ), + (StackEffect::Nop, BytecodeType::StructIndex(Bytecode::Pack)), + ( + StackEffect::Nop, + BytecodeType::StructInstantiationIndex(Bytecode::PackGeneric), + ), ( StackEffect::Nop, - BytecodeType::StructAndLocalIndex(Bytecode::Pack), + BytecodeType::StructIndex(Bytecode::Unpack), ), ( StackEffect::Nop, - BytecodeType::StructAndLocalIndex(Bytecode::Unpack), + BytecodeType::StructInstantiationIndex(Bytecode::UnpackGeneric), ), ( StackEffect::Nop, - BytecodeType::StructAndLocalIndex(Bytecode::Exists), + BytecodeType::StructIndex(Bytecode::Exists), + ), + ( + StackEffect::Nop, + BytecodeType::StructInstantiationIndex(Bytecode::ExistsGeneric), + ), + ( + StackEffect::Add, + BytecodeType::StructIndex(Bytecode::MoveFrom), ), ( StackEffect::Add, - BytecodeType::StructAndLocalIndex(Bytecode::MoveFrom), + BytecodeType::StructInstantiationIndex(Bytecode::MoveFromGeneric), + ), + ( + StackEffect::Sub, + BytecodeType::StructIndex(Bytecode::MoveToSender), ), ( StackEffect::Sub, - BytecodeType::StructAndLocalIndex(Bytecode::MoveToSender), + BytecodeType::StructInstantiationIndex(Bytecode::MoveToSenderGeneric), ), ( StackEffect::Nop, - BytecodeType::StructAndLocalIndex(Bytecode::MutBorrowGlobal), + BytecodeType::StructIndex(Bytecode::MutBorrowGlobal), ), ( StackEffect::Nop, - BytecodeType::StructAndLocalIndex(Bytecode::ImmBorrowGlobal), + BytecodeType::StructInstantiationIndex(Bytecode::MutBorrowGlobalGeneric), ), ( StackEffect::Nop, - BytecodeType::FieldDefinitionIndex(Bytecode::MutBorrowField), + BytecodeType::StructIndex(Bytecode::ImmBorrowGlobal), ), ( StackEffect::Nop, - BytecodeType::FieldDefinitionIndex(Bytecode::ImmBorrowField), + BytecodeType::StructInstantiationIndex(Bytecode::ImmBorrowGlobalGeneric), ), ( StackEffect::Nop, - BytecodeType::FunctionAndLocalIndex(Bytecode::Call), + 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)), @@ -371,22 +425,17 @@ impl<'a> BytecodeGenerator<'a> { Self::index_or_none(&module.byte_array_pool, &mut self.rng) .map(|x| instruction(ByteArrayPoolIndex::new(x))) } - BytecodeType::StructAndLocalIndex(instruction) => { + BytecodeType::StructIndex(instruction) => { // Select a random struct definition and local signature - Self::index_or_none(&module.struct_defs, &mut self.rng).map(|x| { - instruction( - StructDefinitionIndex::new(x), - // Set 0 as the index. This will be filled in/changed later on. - LocalsSignatureIndex::new(0), - ) - }) + Self::index_or_none(&module.struct_defs, &mut self.rng) + .map(|x| instruction(StructDefinitionIndex::new(x))) } - BytecodeType::FieldDefinitionIndex(instruction) => { + BytecodeType::FieldHandleIndex(instruction) => { // Select a field definition from the module's field definitions - Self::index_or_none(&module.field_defs, &mut self.rng) - .map(|x| instruction(FieldDefinitionIndex::new(x))) + Self::index_or_none(&module.field_handles, &mut self.rng) + .map(|x| instruction(FieldHandleIndex::new(x))) } - BytecodeType::FunctionAndLocalIndex(instruction) => { + 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, &mut self.rng) @@ -397,13 +446,22 @@ impl<'a> BytecodeGenerator<'a> { callable_fns[handle_idx as usize], ) }) - .map(|handle| { - instruction( - handle, - // Set 0 as the signature index. This index will be filled in/changed later - LocalsSignatureIndex::new(0), - ) - }) + .map(|handle| instruction(handle)) + } + BytecodeType::StructInstantiationIndex(instruction) => { + // Select a field definition from the module's field definitions + Self::index_or_none(&module.struct_def_instantiations, &mut 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, &mut 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, &mut self.rng) + .map(|x| instruction(FieldInstantiationIndex::new(x))) } }; if let Some(instruction) = instruction { @@ -513,10 +571,27 @@ impl<'a> BytecodeGenerator<'a> { }; match summary.effects { summaries::Effects::TyParams(instantiation, effect, instantiation_application) => { - let instantiation = instantiation(&state); + let (struct_idx, instantiation) = instantiation(&state); let index = state.module.add_instantiation(instantiation); - let effects = effect(index); - let instruction = instantiation_application(index); + 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), @@ -543,7 +618,7 @@ impl<'a> BytecodeGenerator<'a> { let instruction = step.1; debug!("Affected: {}", state); debug!("Actual instr: {:?}", instruction); - if let Bytecode::Call(index, _) = instruction { + if let Bytecode::Call(index) = instruction { state .call_graph .add_call(fn_context.function_handle_index, index); @@ -679,7 +754,7 @@ impl<'a> BytecodeGenerator<'a> { &mut self, fn_context: &mut FunctionGenerationContext, locals: &[SignatureToken], - signature: &FunctionSignature, + fh: &FunctionHandle, acquires_global_resources: &[StructDefinitionIndex], module: &mut CompiledModuleMut, call_graph: &mut CallGraph, @@ -688,7 +763,12 @@ impl<'a> BytecodeGenerator<'a> { // The number of basic blocks must be at least one based on the // generation range. assume!(number_of_blocks > 0); - let mut cfg = CFG::new(&mut self.rng, locals, signature, number_of_blocks); + let mut cfg = CFG::new( + &mut 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!( @@ -698,14 +778,14 @@ impl<'a> BytecodeGenerator<'a> { let state1 = AbstractState::from_locals( module.clone(), block.get_locals_in().clone(), - signature.type_formals.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(), - signature.type_formals.clone(), + fh.type_parameters.clone(), acquires_global_resources.to_vec(), call_graph.clone(), ); @@ -750,7 +830,7 @@ impl<'a> BytecodeGenerator<'a> { )? } else if cfg_copy.num_children(*block_id) == 0 { // Return: Add return types to last block - for token_type in signature.return_types.iter() { + 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!( @@ -792,10 +872,7 @@ impl<'a> BytecodeGenerator<'a> { let mut call_graph = CallGraph::new(module.function_handles.len()); for fdef in fdefs.iter_mut() { let f_handle = &module.function_handles[fdef.function.0 as usize].clone(); - let func_sig = module.function_signatures[f_handle.signature.0 as usize].clone(); - let locals_sigs = module.locals_signatures[fdef.code.locals.0 as usize] - .0 - .clone(); + let locals_sigs = module.signatures[fdef.code.locals.0 as usize].0.clone(); let mut fn_context = FunctionGenerationContext::new( fdef.function, call_graph.max_calling_depth(fdef.function), @@ -805,7 +882,7 @@ impl<'a> BytecodeGenerator<'a> { fdef.code.code = self.generate( &mut fn_context, &locals_sigs, - &func_sig, + &f_handle, &fdef.acquires_global_resources, &mut module, &mut call_graph, @@ -823,12 +900,12 @@ impl<'a> BytecodeGenerator<'a> { token: &SignatureToken, ) -> Vec { match token { - SignatureToken::Address => vec![Bytecode::LdAddr(AddressPoolIndex::new(0))], + SignatureToken::Address => vec![Bytecode::LdAddr(AddressPoolIndex(0))], SignatureToken::U64 => vec![Bytecode::LdU64(0)], SignatureToken::U8 => vec![Bytecode::LdU8(0)], SignatureToken::U128 => vec![Bytecode::LdU128(0)], SignatureToken::Bool => vec![Bytecode::LdFalse], - SignatureToken::Struct(handle_idx, instantiation) => { + SignatureToken::Struct(handle_idx) => { let struct_def_idx = module .module .struct_defs() @@ -840,30 +917,55 @@ impl<'a> BytecodeGenerator<'a> { let struct_def = module .module .struct_def_at(StructDefinitionIndex(struct_def_idx as TableIndex)); - let (num_fields, index) = match struct_def.field_information { + let fields = match &struct_def.field_information { StructFieldInformation::Native => panic!("Can't inhabit native structs"), - StructFieldInformation::Declared { - field_count, - fields, - } => (field_count, fields.0), + StructFieldInformation::Declared(fields) => fields.clone(), }; - let fields = module + 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 - .field_def_range(num_fields, FieldDefinitionIndex(index)) - .to_vec(); + .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::Declared(fields) => fields.clone(), + }; let mut bytecodes: Vec = fields .iter() .flat_map(|field| { - let field_sig_tok = &module.module.type_signature_at(field.signature).0; - let reified_field_sig_tok = field_sig_tok.substitute(&instantiation); + 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()); - bytecodes.push(Bytecode::Pack( - StructDefinitionIndex(struct_def_idx as TableIndex), - instantiation_index, - )); + 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 } _ => unimplemented!("Unsupported inhabitation. Type: {:#?}", token), diff --git a/language/tools/test-generation/src/control_flow_graph.rs b/language/tools/test-generation/src/control_flow_graph.rs index 0ac5d23e4f5b..4de3d2718528 100644 --- a/language/tools/test-generation/src/control_flow_graph.rs +++ b/language/tools/test-generation/src/control_flow_graph.rs @@ -5,7 +5,7 @@ use crate::abstract_state::{AbstractValue, BorrowState}; use libra_logger::debug; use rand::{rngs::StdRng, Rng}; use std::collections::{HashMap, VecDeque}; -use vm::file_format::{Bytecode, FunctionSignature, Kind, SignatureToken}; +use vm::file_format::{Bytecode, Kind, Signature, SignatureToken}; /// This type holds basic block identifiers type BlockIDSize = u16; @@ -69,7 +69,7 @@ impl CFG { pub fn new( rng: &mut StdRng, locals: &[SignatureToken], - signature: &FunctionSignature, + parameters: &Signature, target_blocks: BlockIDSize, ) -> CFG { checked_precondition!(target_blocks > 0, "The CFG must haave at least one block"); @@ -135,7 +135,7 @@ impl CFG { }; // Assign locals to basic blocks assume!(target_blocks == 0 || !cfg.basic_blocks.is_empty()); - CFG::add_locals(&mut cfg, rng, locals, signature.arg_types.len()); + CFG::add_locals(&mut cfg, rng, locals, parameters.0.len()); cfg } @@ -263,7 +263,7 @@ impl CFG { ( i, ( - AbstractValue::new_value(token.clone(), Kind::Unrestricted), + AbstractValue::new_value(token.clone(), Kind::Copyable), borrow_state, ), ) diff --git a/language/tools/test-generation/src/lib.rs b/language/tools/test-generation/src/lib.rs index 595502e37bb1..780c0b333a48 100644 --- a/language/tools/test-generation/src/lib.rs +++ b/language/tools/test-generation/src/lib.rs @@ -32,7 +32,10 @@ use utils::module_generation::generate_module; use vm::{ access::ModuleAccess, errors::VMResult, - file_format::{CompiledModule, CompiledModuleMut, FunctionDefinitionIndex, SignatureToken}, + file_format::{ + CompiledModule, CompiledModuleMut, FunctionDefinitionIndex, Kind, SignatureToken, + StructHandleIndex, + }, transaction_metadata::TransactionMetadata, }; @@ -52,11 +55,11 @@ fn run_vm(module: VerifiedModule) -> VMResult<()> { 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).signature; - module.function_signature_at(sig_idx).clone() + let sig_idx = module.function_handle_at(handle).parameters; + module.signature_at(sig_idx).clone() }; let main_args: Vec = function_signature - .arg_types + .0 .iter() .map(|sig_tok| match sig_tok { SignatureToken::Address => Value::address(AccountAddress::DEFAULT), @@ -344,3 +347,99 @@ pub fn run_generation(args: Args) { thread.join().unwrap(); } } + +pub(crate) fn substitute(token: &SignatureToken, tys: &[SignatureToken]) -> SignatureToken { + use SignatureToken::*; + + match token { + Bool => Bool, + U8 => U8, + U64 => U64, + U128 => U128, + Address => Address, + 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. + assume!((*idx as usize) < tys.len()); + tys[*idx as usize].clone() + } + } +} + +pub fn kind(module: &impl ModuleAccess, ty: &SignatureToken, constraints: &[Kind]) -> Kind { + use SignatureToken::*; + + match ty { + // The primitive types & references have kind unrestricted. + Bool | U8 | U64 | U128 | Address | Reference(_) | MutableReference(_) => Kind::Copyable, + TypeParameter(idx) => constraints[*idx as usize], + Vector(ty) => kind(module, ty, constraints), + Struct(idx) => { + let sh = module.struct_handle_at(*idx); + if sh.is_nominal_resource { + Kind::Resource + } else { + Kind::Copyable + } + } + StructInstantiation(idx, type_args) => { + let sh = module.struct_handle_at(*idx); + if sh.is_nominal_resource { + return Kind::Resource; + } + // Gather the kinds of the type actuals. + let kinds = type_args + .iter() + .map(|ty| kind(module, ty, constraints)) + .collect::>(); + // Derive the kind of the struct. + // - If any of the type actuals is `all`, then the struct is `all`. + // - `all` means some part of the type can be either `resource` or + // `unrestricted`. + // - Therefore it is also impossible to determine the kind of the type as a + // whole, and thus `all`. + // - If none of the type actuals is `all`, then the struct is a resource if + // and only if one of the type actuals is `resource`. + kinds.iter().cloned().fold(Kind::Copyable, Kind::join) + } + } +} + +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, + }, + _ => None, + } +} diff --git a/language/tools/test-generation/src/summaries.rs b/language/tools/test-generation/src/summaries.rs index d08ffb4bee3e..0781e4725ef9 100644 --- a/language/tools/test-generation/src/summaries.rs +++ b/language/tools/test-generation/src/summaries.rs @@ -5,31 +5,43 @@ use crate::{ abstract_state::{AbstractState, AbstractValue, BorrowState, Mutability}, error::VMError, function_instantiation_for_state, state_control_flow, state_create_struct, - state_function_can_acquire_resource, state_local_availability_is, state_local_exists, - state_local_kind_is, 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_popn, state_stack_has, - state_stack_has_integer, state_stack_has_polymorphic_eq, state_stack_has_reference, - state_stack_has_struct, state_stack_is_castable, state_stack_kind_is, - 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_create_struct_from_inst, state_function_can_acquire_resource, + state_local_availability_is, state_local_exists, state_local_kind_is, 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_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_kind_is, 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_has_field, state_stack_struct_popn, - state_stack_unpack_struct, state_struct_is_resource, struct_instantiation_for_state, + 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_inst_is_resource, state_struct_is_resource, struct_instantiation_for_state, transitions::*, unpack_instantiation_for_state, with_ty_param, }; -use vm::file_format::{Bytecode, Kind, LocalsSignatureIndex, SignatureToken}; +use vm::file_format::{ + Bytecode, FunctionHandleIndex, FunctionInstantiationIndex, Kind, 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(LocalsSignatureIndex) -> Vec>; +pub type InstantiableEffect = + dyn Fn(StructDefInstantiationIndex) -> Vec>; +pub type FunctionInstantiableEffect = + dyn Fn(FunctionInstantiationIndex) -> Vec>; -type Instantiation = dyn Fn(&AbstractState) -> Vec; -type InstantiableInstruction = dyn Fn(LocalsSignatureIndex) -> Bytecode; +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>), @@ -38,6 +50,11 @@ pub enum Effects { Box, Box, ), + TyParamsCall( + Box, + Box, + Box, + ), } /// The `Summary` of a bytecode instruction contains a list of `Precondition`s @@ -53,7 +70,7 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { Bytecode::Pop => Summary { preconditions: vec![ state_stack_has!(0, None), - state_stack_kind_is!(0, Kind::Unrestricted), + state_stack_kind_is!(0, Kind::Copyable), state_memory_safe!(Some(0)), ], effects: Effects::NoTyParams(vec![state_stack_pop!()]), @@ -124,7 +141,7 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { Bytecode::CopyLoc(i) => Summary { preconditions: vec![ state_local_exists!(i), - state_local_kind_is!(i, Kind::Unrestricted), + state_local_kind_is!(i, Kind::Copyable), state_local_availability_is!(i, BorrowState::Available), state_memory_safe!(None), ], @@ -148,8 +165,8 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { preconditions: vec![ state_stack_has!(0, None), state_local_exists!(i), - // TODO: This covers storing on an unrestricted local only - state_local_kind_is!(i, Kind::Unrestricted), + // TODO: This covers storing on an copyable local only + state_local_kind_is!(i, Kind::Copyable), state_stack_local_polymorphic_eq!(0, i as usize), state_memory_safe!(Some(0)), ], @@ -259,7 +276,7 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { preconditions: vec![ state_stack_has!(0, None), state_stack_has!(1, None), - state_stack_kind_is!(0, Kind::Unrestricted), + state_stack_kind_is!(0, Kind::Copyable), state_stack_has_polymorphic_eq!(0, 1), state_memory_safe!(Some(0)), state_memory_safe!(Some(1)), @@ -286,38 +303,46 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { SignatureToken::Address, ))]), }, - Bytecode::Pack(i, exact_instantiation) => Summary { - preconditions: vec![state_stack_satisfies_struct_signature!( - i, - exact, - exact_instantiation - )], + 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!(i, exact, exact_instantiation), - with_ty_param!((exact, exact_instantiation) => instantiation, + struct_instantiation_for_state!(idx, exact), + with_ty_param!((exact, idx) => inst, vec![ - state_stack_struct_popn!(i), - state_create_struct!(i, instantiation), + state_stack_struct_inst_popn!(idx), + state_create_struct_from_inst!(inst), state_stack_push_register!(), ] ), - with_ty_param!((exact, exact_instantiation) => instantiation, Bytecode::Pack(i, instantiation)), + with_ty_param!((exact, idx) => inst, Bytecode::PackGeneric(inst)), ), }, - Bytecode::Unpack(i, exact_instantiation) => Summary { - preconditions: vec![state_stack_has_struct!(Some(i))], + 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, exact_instantiation) => instantiation, + with_ty_param!((exact, i) => inst, vec![ state_stack_pop!(), - state_stack_unpack_struct!(i, instantiation) + state_stack_unpack_struct_inst!(inst), ] ), - with_ty_param!((exact, exact_instantiation) => instantiation, Bytecode::Unpack(i, instantiation)), + with_ty_param!((exact, i) => inst, Bytecode::UnpackGeneric(inst)), ), }, - Bytecode::Exists(i, _) => Summary { + Bytecode::Exists(i) => Summary { // The result of `state_struct_is_resource` is represented abstractly // so concrete execution may differ preconditions: vec![ @@ -332,6 +357,21 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { 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_is_resource!(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), @@ -343,6 +383,17 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { 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), @@ -354,7 +405,18 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { state_stack_struct_borrow_field!(i), ]), }, - Bytecode::MutBorrowGlobal(i, instantiation) => Summary { + 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, @@ -365,11 +427,26 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { ], effects: Effects::NoTyParams(vec![ state_stack_pop!(), - state_create_struct!(i, instantiation), + 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_is_resource!(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, instantiation) => Summary { + Bytecode::ImmBorrowGlobal(i) => Summary { preconditions: vec![ state_stack_has!( 0, @@ -380,11 +457,26 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { ], effects: Effects::NoTyParams(vec![ state_stack_pop!(), - state_create_struct!(i, instantiation), + state_create_struct!(i), state_stack_push_register_borrow!(Mutability::Immutable), ]), }, - Bytecode::MoveFrom(i, instantiation) => Summary { + Bytecode::ImmBorrowGlobalGeneric(i) => Summary { + preconditions: vec![ + state_stack_has!( + 0, + Some(AbstractValue::new_primitive(SignatureToken::Address)) + ), + state_struct_inst_is_resource!(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_is_resource!(i), @@ -395,29 +487,59 @@ pub fn instruction_summary(instruction: Bytecode, exact: bool) -> Summary { ], effects: Effects::NoTyParams(vec![ state_stack_pop!(), - state_create_struct!(i, instantiation), + state_create_struct!(i), state_stack_push_register!(), ]), }, - Bytecode::MoveToSender(i, _) => Summary { + Bytecode::MoveFromGeneric(i) => Summary { + preconditions: vec![ + state_function_can_acquire_resource!(), + state_struct_inst_is_resource!(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::MoveToSender(i) => Summary { preconditions: vec![ state_struct_is_resource!(i), - state_stack_has_struct!(Some(i)), + state_stack_has_struct!(i), + state_memory_safe!(None), + ], + effects: Effects::NoTyParams(vec![state_stack_pop!()]), + }, + Bytecode::MoveToSenderGeneric(i) => Summary { + preconditions: vec![ + state_struct_inst_is_resource!(i), + state_stack_has_struct_inst!(i), state_memory_safe!(None), ], effects: Effects::NoTyParams(vec![state_stack_pop!()]), }, - Bytecode::Call(i, exact_instantiation) => Summary { + Bytecode::Call(i) => Summary { preconditions: vec![state_stack_satisfies_function_signature!(i)], - effects: Effects::TyParams( + 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, exact_instantiation) => instantiation, + with_ty_param!((exact, i) => inst, vec![ - state_stack_function_popn!(i), - state_stack_function_call!(i, instantiation), + state_stack_function_inst_popn!(inst), + state_stack_function_inst_call!(i), ] ), - with_ty_param!((exact, exact_instantiation) => instantiation, Bytecode::Call(i, instantiation)), + with_ty_param!((exact, i) => inst, Bytecode::CallGeneric(inst)), ), }, // Control flow instructions are called manually and thus have diff --git a/language/tools/test-generation/src/transitions.rs b/language/tools/test-generation/src/transitions.rs index 6f59faed52d2..2e6b88772989 100644 --- a/language/tools/test-generation/src/transitions.rs +++ b/language/tools/test-generation/src/transitions.rs @@ -5,17 +5,20 @@ use crate::{ abstract_state::{AbstractState, AbstractValue, BorrowState, Mutability}, config::ALLOW_MEMORY_UNSAFE, error::VMError, + get_struct_handle_from_reference, get_type_actuals_from_reference, kind, substitute, }; use vm::{ access::*, file_format::{ - FieldDefinitionIndex, FunctionHandleIndex, Kind, LocalsSignatureIndex, SignatureToken, - StructDefinitionIndex, + FieldHandleIndex, FieldInstantiationIndex, FunctionHandleIndex, FunctionInstantiationIndex, + Kind, SignatureIndex, SignatureToken, StructDefInstantiationIndex, StructDefinitionIndex, + StructFieldInformation, }, - views::{FunctionHandleView, SignatureTokenView, StructDefinitionView, ViewInternals}, + views::{FunctionHandleView, StructDefinitionView, ViewInternals}, }; use std::collections::HashMap; +use vm::file_format::TableIndex; //--------------------------------------------------------------------------- // Type Instantiations from Unification with the Abstract Stack @@ -59,8 +62,12 @@ impl Subst { // 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::Struct(sig1, params1), SignatureToken::Struct(sig2, params2)) => { + ( + SignatureToken::StructInstantiation(sig1, params1), + SignatureToken::StructInstantiation(sig2, params2), + ) => { if sig1 != sig2 { return false; } @@ -91,8 +98,7 @@ impl Subst { /// Given a signature token, returns the kind of this token in the module context, and kind /// instantiation for the function. pub fn kind_for_token(state: &AbstractState, token: &SignatureToken, kinds: &[Kind]) -> Kind { - let context = (&state.module.module.struct_handles()[..], kinds); - SignatureToken::kind(context, token) + kind(&state.module.module, token, kinds) } /// Given a locals signature index, determine the kind for each signature token. Restricted for @@ -104,13 +110,7 @@ pub fn kinds_for_instantiation( ) -> Vec { instantiation .iter() - .map(|token| { - let context = ( - &state.module.module.struct_handles()[..], - &state.instantiation[..], - ); - SignatureToken::kind(context, token) - }) + .map(|token| kind(&state.module.module, token, &state.instantiation[..])) .collect() } @@ -163,7 +163,7 @@ pub fn stack_kind_is(state: &AbstractState, index: usize, kind: Kind) -> bool { /// Determine if the abstract value at `index` has a kind that is a subkind of the kind for the /// instruction kind. e.g. if the instruction takes a type of kind `All` then it is OK to fit in a -/// value with a type of kind `Unrestricted`. +/// value with a type of kind `Copyable`. pub fn stack_kind_is_subkind(state: &AbstractState, index: usize, instruction_kind: Kind) -> bool { if !stack_has(state, index, None) { return false; @@ -346,6 +346,19 @@ pub fn local_place(state: &AbstractState, index: u8) -> Result (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, @@ -353,13 +366,13 @@ pub fn local_place(state: &AbstractState, index: u8) -> Result, + 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_formals = struct_def.type_formals(); + let type_parameters = struct_def.type_parameters(); let field_token_views = struct_def .fields() .into_iter() @@ -369,12 +382,12 @@ pub fn stack_satisfies_struct_signature( let mut substitution = Subst::new(); for (i, token_view) in field_token_views.rev().enumerate() { let ty = if let Some(subst) = &instantiation { - token_view.as_inner().substitute(subst) + substitute(token_view.as_inner(), subst) } else { token_view.as_inner().clone() }; let has = if let SignatureToken::TypeParameter(idx) = &ty { - if stack_kind_is_subkind(state, i, type_formals[*idx as usize]) { + if stack_kind_is_subkind(state, i, type_parameters[*idx as usize]) { let stack_tok = state.stack_peek(i).unwrap(); substitution.check_and_add(state, stack_tok.token, ty) } else { @@ -383,7 +396,7 @@ pub fn stack_satisfies_struct_signature( } else { let abstract_value = AbstractValue { token: ty, - kind: token_view.kind(type_formals), + kind: kind(&state.module.module, token_view.as_inner(), type_parameters), }; stack_has(state, i, Some(abstract_value)) }; @@ -397,21 +410,28 @@ pub fn stack_satisfies_struct_signature( pub fn get_struct_instantiation_for_state( state: &AbstractState, - struct_index: StructDefinitionIndex, - instantiation: Option, -) -> Vec { - if let Some(index) = instantiation { - return state.module.instantiantiation_at(index).clone(); + 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 mut partial_instantiation = - stack_satisfies_struct_signature(state, struct_index, instantiation).1; + 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_formals(); + let typs = struct_def.type_parameters(); for (index, kind) in typs.iter().enumerate() { if !partial_instantiation.subst.contains_key(&index) { match kind { - Kind::All | Kind::Unrestricted => { + Kind::All | Kind::Copyable => { partial_instantiation .subst .insert(index, SignatureToken::U64); @@ -422,28 +442,21 @@ pub fn get_struct_instantiation_for_state( } } } - partial_instantiation.instantiation() + (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: Option, -) -> bool { +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, _) => match struct_index { - Some(struct_index) => { - let struct_def = state.module.module.struct_def_at(struct_index); - return struct_handle == struct_def.struct_handle; - } - None => { - return true; - } - }, + 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; + } _ => return false, } } @@ -451,20 +464,44 @@ pub fn stack_has_struct( 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_is_resource(state: &AbstractState, struct_index: StructDefinitionIndex) -> bool { let struct_def = state.module.module.struct_def_at(struct_index); StructDefinitionView::new(&state.module.module, struct_def).is_nominal_resource() } -pub fn stack_struct_has_field(state: &AbstractState, field_index: FieldDefinitionIndex) -> bool { - if let Some(struct_handle_index) = state.stack_peek(0).and_then(|abstract_value| { - SignatureToken::get_struct_handle_from_reference(&abstract_value.token) - }) { - return state - .module - .module - .is_field_in_struct(field_index, struct_handle_index); +pub fn struct_inst_is_resource( + state: &AbstractState, + struct_index: StructDefInstantiationIndex, +) -> bool { + let struct_inst = state.module.module.struct_instantiation_at(struct_index); + struct_is_resource(state, struct_inst.def) +} + +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 } @@ -492,6 +529,17 @@ pub fn stack_has_reference(state: &AbstractState, index: usize, mutability: Muta 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( @@ -508,29 +556,56 @@ pub fn stack_struct_popn( 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: LocalsSignatureIndex, + 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 ty_instantiation = state.module.instantiantiation_at(instantiation); - let sig_tok = SignatureToken::Struct(struct_def.struct_handle, ty_instantiation.clone()); + 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 = kind_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) -> Vec { +pub fn stack_unpack_struct_instantiation( + state: &AbstractState, +) -> (StructDefinitionIndex, Vec) { if let Some(av) = state.stack_peek(0) { match av.token { - SignatureToken::Struct(_, toks) => toks, + 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"), + } + } _ => panic!("Invalid unpack -- non-struct value found at top of stack"), } } else { @@ -538,15 +613,26 @@ pub fn stack_unpack_struct_instantiation(state: &AbstractState) -> Vec 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: LocalsSignatureIndex, + instantiation: Option, ) -> Result { let state_copy = state.clone(); let mut state = state.clone(); - let ty_instantiation = state.module.instantiantiation_at(instantiation).clone(); + let ty_instantiation = match instantiation { + Some(inst) => state.module.instantiantiation_at(inst).clone(), + None => vec![], + }; let kinds = kinds_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); @@ -557,7 +643,7 @@ pub fn stack_unpack_struct( .map(|field| field.type_signature().token()); for token_view in token_views { let abstract_value = AbstractValue { - token: token_view.as_inner().substitute(&ty_instantiation), + token: substitute(token_view.as_inner(), &ty_instantiation), kind: kind_for_token(&state, &token_view.as_inner(), &kinds), }; state = stack_push(&state, abstract_value)?; @@ -567,8 +653,8 @@ pub fn stack_unpack_struct( pub fn struct_ref_instantiation(state: &mut AbstractState) -> Result, VMError> { let token = state.register_move().unwrap().token; - if let Some(type_actuals) = token.get_type_actuals_from_reference() { - Ok(type_actuals.to_vec()) + if let Some(type_actuals) = get_type_actuals_from_reference(&token) { + Ok(type_actuals) } else { Err(VMError::new("Invalid field borrow".to_string())) } @@ -577,18 +663,23 @@ pub fn struct_ref_instantiation(state: &mut AbstractState) -> Result Result { let mut state = state.clone(); let typs = struct_ref_instantiation(&mut state)?; let kinds = kinds_for_instantiation(&state, &typs); - let field_signature = state - .module - .module - .get_field_signature(field_index) - .0 - .clone(); - let reified_field_sig = field_signature.substitute(&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::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 @@ -601,6 +692,14 @@ pub fn stack_struct_borrow_field( 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 { @@ -672,25 +771,22 @@ pub fn stack_satisfies_function_signature( ) -> (bool, Subst) { let state_copy = state.clone(); let function_handle = state_copy.module.module.function_handle_at(function_index); - let function_signature = state_copy - .module - .module - .function_signature_at(function_handle.signature); - let type_formals = &function_signature.type_formals; + let type_parameters = &function_handle.type_parameters; let mut satisfied = true; let mut substitution = Subst::new(); - for (i, arg_type) in function_signature.arg_types.iter().rev().enumerate() { - let has = if let SignatureToken::TypeParameter(idx) = arg_type { - if stack_kind_is_subkind(state, i, type_formals[*idx as usize]) { + 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_kind_is_subkind(state, i, type_parameters[*idx as usize]) { let stack_tok = state.stack_peek(i).unwrap(); - substitution.check_and_add(state, stack_tok.token, arg_type.clone()) + substitution.check_and_add(state, stack_tok.token, parameter.clone()) } else { false } } else { - let kind = SignatureTokenView::new(&state.module.module, arg_type).kind(type_formals); + let kind = kind(&state.module.module, parameter, type_parameters); let abstract_value = AbstractValue { - token: arg_type.clone(), + token: parameter.clone(), kind, }; stack_has(&state, i, Some(abstract_value)) @@ -702,6 +798,17 @@ pub fn stack_satisfies_function_signature( (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() @@ -711,20 +818,20 @@ pub fn function_can_acquire_resource(state: &AbstractState) -> bool { pub fn stack_function_call( state: &AbstractState, function_index: FunctionHandleIndex, - instantiation: LocalsSignatureIndex, + 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 function_signature = state_copy - .module - .module - .function_signature_at(function_handle.signature); - let ty_instantiation = state_copy.module.instantiantiation_at(instantiation); - let kinds = kinds_for_instantiation(&state_copy, &ty_instantiation); - for return_type in function_signature.return_types.iter() { + 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 kinds = kinds_for_instantiation(&state_copy, ty_instantiation); + for return_type in return_.0.iter() { let abstract_value = AbstractValue { - token: return_type.substitute(&ty_instantiation), + token: substitute(return_type, &ty_instantiation), kind: kind_for_token(&state, return_type, &kinds), }; state = stack_push(&state, abstract_value)?; @@ -732,19 +839,33 @@ pub fn stack_function_call( 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: FunctionHandleIndex, -) -> Vec { - let mut partial_instantiation = stack_satisfies_function_signature(state, function_index).1; - let function_handle = state.module.module.function_handle_at(function_index); + 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 signature = function_handle.signature(); - let typs = signature.type_formals(); + let typs = function_handle.type_parameters(); for (index, kind) in typs.iter().enumerate() { if !partial_instantiation.subst.contains_key(&index) { match kind { - Kind::All | Kind::Unrestricted => { + Kind::All | Kind::Copyable => { partial_instantiation .subst .insert(index, SignatureToken::U64); @@ -755,7 +876,7 @@ pub fn get_function_instantiation_for_state( } } } - partial_instantiation.instantiation() + (func_inst.handle, partial_instantiation.instantiation()) } /// Pop the number of stack values required to call the function @@ -767,17 +888,25 @@ pub fn stack_function_popn( let state_copy = state.clone(); let mut state = state.clone(); let function_handle = state_copy.module.module.function_handle_at(function_index); - let function_signature = state_copy - .module - .module - .function_signature_at(function_handle.signature); - let number_of_pops = function_signature.arg_types.iter().len(); + 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 @@ -957,14 +1086,20 @@ macro_rules! state_stack_ref_polymorphic_eq { /// `state` needs to be given. #[macro_export] macro_rules! state_stack_satisfies_struct_signature { - ($e: expr, $is_exact: expr, $exact_instantiation: expr) => { - Box::new(move |state| { - if $is_exact { - stack_satisfies_struct_signature(state, $e, Some($exact_instantiation)).0 - } else { - stack_satisfies_struct_signature(state, $e, None).0 - } - }) + ($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)) }; } @@ -981,8 +1116,15 @@ macro_rules! state_stack_struct_popn { /// `state` needs to be given. #[macro_export] macro_rules! state_create_struct { - ($e1: expr, $e2: expr) => { - Box::new(move |state| create_struct(state, $e1, $e2)) + ($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)) }; } @@ -995,12 +1137,26 @@ macro_rules! state_stack_has_struct { }; } +#[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, $instantiation: expr) => { - Box::new(move |state| stack_unpack_struct(state, $e, $instantiation)) + ($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)) }; } @@ -1013,6 +1169,13 @@ macro_rules! state_struct_is_resource { }; } +#[macro_export] +macro_rules! state_struct_inst_is_resource { + ($e: expr) => { + Box::new(move |state| struct_inst_is_resource(state, $e)) + }; +} + /// Wrapper for enclosing the arguments of `struct_has_field` so that only the /// `state` needs to be given. #[macro_export] @@ -1022,6 +1185,13 @@ macro_rules! state_stack_struct_has_field { }; } +#[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] @@ -1031,6 +1201,13 @@ macro_rules! state_stack_struct_borrow_field { }; } +#[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] @@ -1067,6 +1244,13 @@ macro_rules! state_stack_satisfies_function_signature { }; } +#[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] @@ -1076,12 +1260,26 @@ macro_rules! state_stack_function_popn { }; } +#[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, $instantiation: expr) => { - Box::new(move |state| stack_function_call(state, $e, $instantiation)) + ($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)) }; } @@ -1146,14 +1344,8 @@ macro_rules! state_control_flow { /// Determine the proper type instantiation for struct in the current state. #[macro_export] macro_rules! struct_instantiation_for_state { - ($e: expr, $is_exact: expr, $exact_instantiation: expr) => { - Box::new(move |state| { - if $is_exact { - get_struct_instantiation_for_state(state, $e, Some($exact_instantiation)) - } else { - get_struct_instantiation_for_state(state, $e, None) - } - }) + ($e: expr, $is_exact: expr) => { + Box::new(move |state| get_struct_instantiation_for_state(state, $e, $is_exact)) }; } @@ -1169,9 +1361,13 @@ macro_rules! unpack_instantiation_for_state { /// if the instantiation should be inferred from the current state. #[macro_export] macro_rules! with_ty_param { - (($is_exact: expr, $exact_instantiation: expr) => $ty:ident, $body:expr) => { - Box::new(move |$ty| { - let $ty = if $is_exact { $exact_instantiation } else { $ty }; + (($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/language/tools/test-generation/tests/common.rs b/language/tools/test-generation/tests/common.rs index 585e1d5d15a3..828a475aba1b 100644 --- a/language/tools/test-generation/tests/common.rs +++ b/language/tools/test-generation/tests/common.rs @@ -7,7 +7,7 @@ use test_generation::{ config::ALLOW_MEMORY_UNSAFE, summaries::{instruction_summary, Effects}, }; -use vm::file_format::Bytecode; +use vm::file_format::{Bytecode, FunctionInstantiation, StructDefInstantiation}; pub fn run_instruction( instruction: Bytecode, @@ -29,10 +29,15 @@ pub fn run_instruction( | Bytecode::WriteRef | Bytecode::FreezeRef | Bytecode::MutBorrowField(_) + | Bytecode::MutBorrowFieldGeneric(_) + | Bytecode::ImmBorrowFieldGeneric(_) | Bytecode::ImmBorrowField(_) - | Bytecode::MutBorrowGlobal(_, _) - | Bytecode::ImmBorrowGlobal(_, _) - | Bytecode::MoveToSender(_, _) => { + | Bytecode::MutBorrowGlobal(_) + | Bytecode::MutBorrowGlobalGeneric(_) + | Bytecode::ImmBorrowGlobal(_) + | Bytecode::ImmBorrowGlobalGeneric(_) + | Bytecode::MoveToSender(_) + | Bytecode::MoveToSenderGeneric(_) => { let len = summary.preconditions.len(); summary.preconditions[..(len - 1)] .iter() @@ -61,10 +66,33 @@ pub fn run_instruction( ); match summary.effects { Effects::TyParams(instantiation, effect, instantiation_application) => { - let instantiation = instantiation(&initial_state); + let (struct_idx, instantiation) = instantiation(&initial_state); let index = initial_state.module.add_instantiation(instantiation); - let effects = effect(index); - let instruction = instantiation_application(index); + 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) diff --git a/language/tools/test-generation/tests/control_flow_instructions.rs b/language/tools/test-generation/tests/control_flow_instructions.rs index 7201e17da484..bbe0c20807a1 100644 --- a/language/tools/test-generation/tests/control_flow_instructions.rs +++ b/language/tools/test-generation/tests/control_flow_instructions.rs @@ -7,41 +7,30 @@ use std::collections::HashMap; use test_generation::abstract_state::{AbstractState, AbstractValue, CallGraph}; use vm::file_format::{ empty_module, Bytecode, CompiledModuleMut, FunctionHandle, FunctionHandleIndex, - FunctionSignature, FunctionSignatureIndex, IdentifierIndex, LocalsSignature, - LocalsSignatureIndex, ModuleHandleIndex, SignatureToken, + IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, }; mod common; fn generate_module_with_function() -> CompiledModuleMut { let mut module: CompiledModuleMut = empty_module(); + let offset = module.identifiers.len(); - let function_sig_offset = module.function_signatures.len(); module.identifiers.push(Identifier::new("func0").unwrap()); - let sigs = vec![( - vec![], - FunctionSignature { - arg_types: vec![SignatureToken::U64, SignatureToken::Bool], - return_types: vec![SignatureToken::Address], - type_formals: vec![], - }, - )]; + module.signatures = vec![ + Signature(vec![]), + Signature(vec![SignatureToken::U64, SignatureToken::Bool]), + Signature(vec![SignatureToken::Address]), + ]; - module.function_handles = sigs - .iter() - .enumerate() - .map(|(i, _)| FunctionHandle { - name: IdentifierIndex::new((i + offset) as u16), - signature: FunctionSignatureIndex::new((i + function_sig_offset) as u16), - module: ModuleHandleIndex::new(0), - }) - .collect(); - let (local_sigs, mut function_sigs): (Vec<_>, Vec<_>) = sigs.into_iter().unzip(); - module.function_signatures.append(&mut function_sigs); - module - .locals_signatures - .append(&mut local_sigs.into_iter().map(LocalsSignature).collect()); + 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![], + }]; module } @@ -52,10 +41,7 @@ fn bytecode_call() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + let (state2, _) = common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); assert_eq!( state2.stack_peek(0), Some(AbstractValue::new_primitive(SignatureToken::Address)), @@ -69,10 +55,7 @@ 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); } #[test] @@ -83,9 +66,6 @@ fn bytecode_call_return_not_pushed() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + let (state2, _) = common::run_instruction(Bytecode::Call(FunctionHandleIndex::new(0)), state1); assert_eq!(state2.stack_len(), 0,); } diff --git a/language/tools/test-generation/tests/local_instructions.rs b/language/tools/test-generation/tests/local_instructions.rs index d8a26a9042cf..a381aaea28d0 100644 --- a/language/tools/test-generation/tests/local_instructions.rs +++ b/language/tools/test-generation/tests/local_instructions.rs @@ -106,7 +106,7 @@ fn bytecode_mutborrowloc() { state2.stack_peek(0), Some(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted + Kind::Copyable )), "stack type postcondition not met" ); @@ -133,7 +133,7 @@ fn bytecode_immborrowloc() { state2.stack_peek(0), Some(AbstractValue::new_reference( SignatureToken::Reference(Box::new(SignatureToken::U64),), - Kind::Unrestricted + Kind::Copyable )), "stack type postcondition not met" ); diff --git a/language/tools/test-generation/tests/reference_instructions.rs b/language/tools/test-generation/tests/reference_instructions.rs index 6758f5dcde2c..b3316f34c932 100644 --- a/language/tools/test-generation/tests/reference_instructions.rs +++ b/language/tools/test-generation/tests/reference_instructions.rs @@ -12,7 +12,7 @@ fn bytecode_readref() { let mut state1 = AbstractState::new(); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); let (state2, _) = common::run_instruction(Bytecode::ReadRef, state1); assert_eq!( @@ -35,7 +35,7 @@ fn bytecode_readref_wrong_dereference() { let mut state1 = AbstractState::new(); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); let (state2, _) = common::run_instruction(Bytecode::ReadRef, state1); assert!( @@ -50,7 +50,7 @@ fn bytecode_writeref() { state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); let (state2, _) = common::run_instruction(Bytecode::WriteRef, state1); assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); @@ -63,7 +63,7 @@ fn bytecode_writeref_type_mismatch() { state1.stack_push(AbstractValue::new_primitive(SignatureToken::Bool)); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); common::run_instruction(Bytecode::WriteRef, state1); } @@ -75,7 +75,7 @@ fn bytecode_writeref_stack_len_mismatch() { state1.stack_push(AbstractValue::new_primitive(SignatureToken::U64)); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); let (state2, _) = common::run_instruction(Bytecode::WriteRef, state1); assert!(state2.stack_len() != 0, "stack type postcondition not met"); @@ -86,7 +86,7 @@ fn bytecode_feezeref() { let mut state1 = AbstractState::new(); state1.stack_push(AbstractValue::new_reference( SignatureToken::MutableReference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); let (state2, _) = common::run_instruction(Bytecode::FreezeRef, state1); assert_eq!(state2.stack_len(), 1, "stack len postcondition not met"); @@ -94,7 +94,7 @@ fn bytecode_feezeref() { state2.stack_peek(0), Some(AbstractValue::new_reference( SignatureToken::Reference(Box::new(SignatureToken::U64)), - Kind::Unrestricted + Kind::Copyable )), "stack type postcondition not met" ); @@ -113,7 +113,7 @@ fn bytecode_feezeref_already_immutable() { let mut state1 = AbstractState::new(); state1.stack_push(AbstractValue::new_reference( SignatureToken::Reference(Box::new(SignatureToken::U64)), - Kind::Unrestricted, + Kind::Copyable, )); common::run_instruction(Bytecode::FreezeRef, state1); } diff --git a/language/tools/test-generation/tests/struct_instructions.rs b/language/tools/test-generation/tests/struct_instructions.rs index abbf98d1b645..43dfbdda0427 100644 --- a/language/tools/test-generation/tests/struct_instructions.rs +++ b/language/tools/test-generation/tests/struct_instructions.rs @@ -4,67 +4,52 @@ extern crate test_generation; use move_core_types::identifier::Identifier; use std::collections::HashMap; -use test_generation::abstract_state::{AbstractState, AbstractValue, CallGraph}; +use test_generation::{ + abstract_state::{AbstractState, AbstractValue, CallGraph}, + kind, +}; use vm::{ access::ModuleAccess, file_format::{ - empty_module, Bytecode, CompiledModule, CompiledModuleMut, FieldDefinition, - FieldDefinitionIndex, IdentifierIndex, Kind, LocalsSignatureIndex, MemberCount, - ModuleHandleIndex, SignatureToken, StructDefinition, StructDefinitionIndex, - StructFieldInformation, StructHandle, StructHandleIndex, TableIndex, TypeSignature, - TypeSignatureIndex, + empty_module, Bytecode, CompiledModule, CompiledModuleMut, FieldDefinition, FieldHandle, + FieldHandleIndex, IdentifierIndex, Kind, ModuleHandleIndex, SignatureToken, + StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandle, + StructHandleIndex, TableIndex, TypeSignature, }, - views::{SignatureTokenView, StructDefinitionView, ViewInternals}, + views::{StructDefinitionView, ViewInternals}, }; mod common; fn generate_module_with_struct(resource: bool) -> CompiledModuleMut { let mut module: CompiledModuleMut = empty_module(); - module.type_signatures = vec![ - SignatureToken::Bool, - SignatureToken::U64, - SignatureToken::Vector(Box::new(SignatureToken::U8)), - SignatureToken::Address, - ] - .into_iter() - .map(TypeSignature) - .collect(); let struct_index = 0; let num_fields = 5; let offset = module.identifiers.len() as TableIndex; module.identifiers.push(Identifier::new("struct0").unwrap()); - let field_information = StructFieldInformation::Declared { - field_count: num_fields as MemberCount, - fields: FieldDefinitionIndex::new(module.field_defs.len() as TableIndex), - }; - let struct_def = StructDefinition { - struct_handle: StructHandleIndex(struct_index), - field_information, - }; - module.struct_defs.push(struct_def); - + let mut fields = vec![]; for i in 0..num_fields { module .identifiers .push(Identifier::new(format!("string{}", i)).unwrap()); - let struct_handle_idx = StructHandleIndex::new(struct_index); - let typ_idx = TypeSignatureIndex::new(0); let str_pool_idx = IdentifierIndex::new(i + 1 as TableIndex); - let field_def = FieldDefinition { - struct_: struct_handle_idx, + fields.push(FieldDefinition { name: str_pool_idx, - signature: typ_idx, - }; - module.field_defs.push(field_def); + 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), is_nominal_resource: resource, - type_formals: vec![], + type_parameters: vec![], }]; module } @@ -81,36 +66,42 @@ fn create_struct_value(module: &CompiledModule) -> (AbstractValue, Vec Kind::All, - (Kind::Resource, _) | (_, Kind::Resource) => Kind::Resource, - (Kind::Unrestricted, Kind::Unrestricted) => Kind::Unrestricted, - } - }) + tokens.iter().map(|token| kind(module, token, &[])).fold( + Kind::Copyable, + |acc_kind, next_kind| match (acc_kind, next_kind) { + (Kind::All, _) | (_, Kind::All) => Kind::All, + (Kind::Resource, _) | (_, Kind::Resource) => Kind::Resource, + (Kind::Copyable, Kind::Copyable) => Kind::Copyable, + }, + ) }; ( AbstractValue::new_struct( - SignatureToken::Struct(struct_def.struct_handle, vec![]), + SignatureToken::Struct(struct_def.struct_handle), struct_kind, ), tokens, ) } +fn get_field_signature<'a>( + module: &'a CompiledModuleMut, + 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, + } +} + #[test] #[should_panic] fn bytecode_pack_signature_not_satisfied() { let module: CompiledModuleMut = 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::Pack(StructDefinitionIndex::new(0)), state1); } #[test] @@ -122,14 +113,12 @@ fn bytecode_pack() { for token in tokens { let abstract_value = AbstractValue { token: token.clone(), - kind: SignatureTokenView::new(&state1.module.module, &token).kind(&[]), + kind: kind(&state1.module.module, &token, &[]), }; state1.stack_push(abstract_value); } - let (state2, _) = common::run_instruction( - Bytecode::Pack(StructDefinitionIndex::new(0), LocalsSignatureIndex::new(0)), - state1, - ); + 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, @@ -143,10 +132,7 @@ fn bytecode_unpack_signature_not_satisfied() { let module: CompiledModuleMut = 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::Unpack(StructDefinitionIndex::new(0)), state1); } #[test] @@ -156,10 +142,8 @@ fn bytecode_unpack() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + let (state2, _) = + common::run_instruction(Bytecode::Unpack(StructDefinitionIndex::new(0)), state1); assert_eq!( state2.stack_len(), tokens.len(), @@ -173,10 +157,8 @@ fn bytecode_exists() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + let (state2, _) = + common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); assert_eq!( state2.stack_peek(0), Some(AbstractValue::new_primitive(SignatureToken::Bool)), @@ -191,10 +173,7 @@ fn bytecode_exists_struct_is_not_resource() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); } #[test] @@ -203,10 +182,7 @@ fn bytecode_exists_no_address_on_stack() { let module: CompiledModuleMut = 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::Exists(StructDefinitionIndex::new(0)), state1); } #[test] @@ -225,13 +201,11 @@ fn bytecode_movefrom() { .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), LocalsSignatureIndex::new(0)), - state1, - ); + 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), + matches!(struct_value.token, SignatureToken::Struct(struct_handle) if struct_handle == struct_def.struct_handle), "stack type postcondition not met" ); } @@ -243,10 +217,7 @@ fn bytecode_movefrom_struct_is_not_resource() { 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::MoveFrom(StructDefinitionIndex::new(0)), state1); } #[test] @@ -255,10 +226,7 @@ fn bytecode_movefrom_no_address_on_stack() { let module: CompiledModuleMut = 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), LocalsSignatureIndex::new(0)), - state1, - ); + common::run_instruction(Bytecode::MoveFrom(StructDefinitionIndex::new(0)), state1); } #[test] @@ -268,7 +236,7 @@ fn bytecode_movetosender() { AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); state1.stack_push(create_struct_value(&state1.module.module).0); let (state2, _) = common::run_instruction( - Bytecode::MoveToSender(StructDefinitionIndex::new(0), LocalsSignatureIndex::new(0)), + Bytecode::MoveToSender(StructDefinitionIndex::new(0)), state1, ); assert_eq!(state2.stack_len(), 0, "stack type postcondition not met"); @@ -282,7 +250,7 @@ fn bytecode_movetosender_struct_is_not_resource() { AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); state1.stack_push(create_struct_value(&state1.module.module).0); common::run_instruction( - Bytecode::MoveToSender(StructDefinitionIndex::new(0), LocalsSignatureIndex::new(0)), + Bytecode::MoveToSender(StructDefinitionIndex::new(0)), state1, ); } @@ -294,14 +262,23 @@ fn bytecode_movetosender_no_struct_on_stack() { let state1 = AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); common::run_instruction( - Bytecode::MoveToSender(StructDefinitionIndex::new(0), LocalsSignatureIndex::new(0)), + Bytecode::MoveToSender(StructDefinitionIndex::new(0)), state1, ); } #[test] fn bytecode_mutborrowfield() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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; @@ -309,19 +286,13 @@ fn bytecode_mutborrowfield() { token: SignatureToken::MutableReference(Box::new(struct_value.token)), kind: struct_value.kind, }); - let field_index = FieldDefinitionIndex::new(0); - let (state2, _) = common::run_instruction(Bytecode::MutBorrowField(field_index), state1); - let field_signature = state2 - .module - .module - .get_field_signature(field_index) - .0 - .clone(); + let (state2, _) = common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); + let kind = kind(&state2.module.module, &field_signature, &[]); assert_eq!( state2.stack_peek(0), Some(AbstractValue { - token: SignatureToken::MutableReference(Box::new(field_signature.clone())), - kind: SignatureTokenView::new(&state2.module.module, &field_signature).kind(&[]), + token: SignatureToken::MutableReference(Box::new(field_signature)), + kind, }), "stack type postcondition not met" ); @@ -330,17 +301,30 @@ fn bytecode_mutborrowfield() { #[test] #[should_panic] fn bytecode_mutborrowfield_stack_has_no_reference() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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)); - let field_index = FieldDefinitionIndex::new(0); - common::run_instruction(Bytecode::MutBorrowField(field_index), state1); + common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); } #[test] #[should_panic] fn bytecode_mutborrowfield_ref_is_immutable() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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; @@ -348,13 +332,21 @@ fn bytecode_mutborrowfield_ref_is_immutable() { token: SignatureToken::Reference(Box::new(struct_value.token)), kind: struct_value.kind, }); - let field_index = FieldDefinitionIndex::new(0); - common::run_instruction(Bytecode::MutBorrowField(field_index), state1); + common::run_instruction(Bytecode::MutBorrowField(field_handle_idx), state1); } #[test] fn bytecode_immborrowfield() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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; @@ -362,19 +354,13 @@ fn bytecode_immborrowfield() { token: SignatureToken::Reference(Box::new(struct_value.token)), kind: struct_value.kind, }); - let field_index = FieldDefinitionIndex::new(0); - let (state2, _) = common::run_instruction(Bytecode::ImmBorrowField(field_index), state1); - let field_signature = state2 - .module - .module - .get_field_signature(field_index) - .0 - .clone(); + let (state2, _) = common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); + let kind = kind(&state2.module.module, &field_signature, &[]); assert_eq!( state2.stack_peek(0), Some(AbstractValue { - token: SignatureToken::MutableReference(Box::new(field_signature.clone())), - kind: SignatureTokenView::new(&state2.module.module, &field_signature).kind(&[]), + token: SignatureToken::MutableReference(Box::new(field_signature)), + kind, }), "stack type postcondition not met" ); @@ -383,17 +369,30 @@ fn bytecode_immborrowfield() { #[test] #[should_panic] fn bytecode_immborrowfield_stack_has_no_reference() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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)); - let field_index = FieldDefinitionIndex::new(0); - common::run_instruction(Bytecode::ImmBorrowField(field_index), state1); + common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); } #[test] #[should_panic] fn bytecode_immborrowfield_ref_is_mutable() { - let module: CompiledModuleMut = generate_module_with_struct(false); + let mut module: CompiledModuleMut = 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; @@ -401,8 +400,7 @@ fn bytecode_immborrowfield_ref_is_mutable() { token: SignatureToken::MutableReference(Box::new(struct_value.token)), kind: struct_value.kind, }); - let field_index = FieldDefinitionIndex::new(0); - common::run_instruction(Bytecode::ImmBorrowField(field_index), state1); + common::run_instruction(Bytecode::ImmBorrowField(field_handle_idx), state1); } #[test] @@ -413,7 +411,7 @@ fn bytecode_borrowglobal() { 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), LocalsSignatureIndex::new(0)), + Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), state1, ); assert_eq!( @@ -434,7 +432,7 @@ fn bytecode_borrowglobal_struct_is_not_resource() { 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), LocalsSignatureIndex::new(0)), + Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), state1, ); } @@ -446,7 +444,7 @@ fn bytecode_borrowglobal_no_address_on_stack() { let state1 = AbstractState::from_locals(module, HashMap::new(), vec![], vec![], CallGraph::new(0)); common::run_instruction( - Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0), LocalsSignatureIndex::new(0)), + Bytecode::MutBorrowGlobal(StructDefinitionIndex::new(0)), state1, ); } diff --git a/language/tools/utils/src/module_generation/generator.rs b/language/tools/utils/src/module_generation/generator.rs index de8cea18f72f..12a9d8f3ec93 100644 --- a/language/tools/utils/src/module_generation/generator.rs +++ b/language/tools/utils/src/module_generation/generator.rs @@ -159,7 +159,7 @@ impl<'a> ModuleGenerator<'a> { } } - fn type_formals(&mut self) -> Vec<(TypeVar, Kind)> { + fn type_parameters(&mut self) -> Vec<(TypeVar, Kind)> { // Don't generate type parameters if we're generating simple types only if self.options.simple_types_only { vec![] @@ -169,7 +169,7 @@ impl<'a> ModuleGenerator<'a> { num_ty_params, ( Spanned::unsafe_no_loc(TypeVar_::new(self.identifier())), - Kind::Unrestricted, + Kind::Copyable, ) ) } @@ -178,7 +178,7 @@ impl<'a> ModuleGenerator<'a> { // All functions will have unit return type, and an empty body with the exception of a return. // We'll scoop this out and replace it later on in the compiled module that we generate. fn function_signature(&mut self) -> FunctionSignature { - let ty_params = self.type_formals(); + let ty_params = self.type_parameters(); let number_of_args = self.index(self.options.max_function_call_size); let mut formals: Vec<(Var, Type)> = init!(number_of_args, { let param_name = Spanned::unsafe_no_loc(Var_::new(self.identifier())); @@ -247,12 +247,12 @@ impl<'a> ModuleGenerator<'a> { fn struct_def(&mut self, is_nominal_resource: bool) { let name = StructName::new(self.identifier()); - let type_formals = self.type_formals(); - let fields = self.struct_fields(&type_formals); + let type_parameters = self.type_parameters(); + let fields = self.struct_fields(&type_parameters); let strct = StructDefinition_ { is_nominal_resource, name, - type_formals, + type_formals: type_parameters, fields, invariants: vec![], }; diff --git a/language/tools/utils/src/module_generation/padding.rs b/language/tools/utils/src/module_generation/padding.rs index 8c6255672f05..cefc56fd6294 100644 --- a/language/tools/utils/src/module_generation/padding.rs +++ b/language/tools/utils/src/module_generation/padding.rs @@ -5,7 +5,7 @@ use crate::module_generation::{options::ModuleGeneratorOptions, utils::random_st use libra_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use rand::{rngs::StdRng, Rng, SeedableRng}; -use vm::file_format::{Bytecode, CompiledModuleMut, LocalsSignature}; +use vm::file_format::{Bytecode, CompiledModuleMut, Signature}; /////////////////////////////////////////////////////////////////////////// // Padding of tables in compiled modules @@ -28,7 +28,7 @@ impl Pad { slf.pad_address_table(module); slf.pad_identifier_table(module); slf.pad_byte_array_table(module); - slf.pad_locals_signatures(module); + slf.pad_signatures(module); slf.pad_function_bodies(module); } @@ -69,9 +69,9 @@ impl Pad { } // Ensure that locals signatures always contain an empty signature - fn pad_locals_signatures(&mut self, module: &mut CompiledModuleMut) { - if module.locals_signatures.iter().all(|v| !v.is_empty()) { - module.locals_signatures.push(LocalsSignature(Vec::new())); + fn pad_signatures(&mut self, module: &mut CompiledModuleMut) { + if module.signatures.iter().all(|v| !v.is_empty()) { + module.signatures.push(Signature(Vec::new())); } } } diff --git a/language/tools/vm-genesis/genesis/genesis.blob b/language/tools/vm-genesis/genesis/genesis.blob index 1c3c00d8c38a..1143c3712119 100644 Binary files a/language/tools/vm-genesis/genesis/genesis.blob and b/language/tools/vm-genesis/genesis/genesis.blob differ diff --git a/language/tools/vm-genesis/src/genesis_gas_schedule.rs b/language/tools/vm-genesis/src/genesis_gas_schedule.rs index a39bddee09c8..426246a3a65a 100644 --- a/language/tools/vm-genesis/src/genesis_gas_schedule.rs +++ b/language/tools/vm-genesis/src/genesis_gas_schedule.rs @@ -10,8 +10,9 @@ use move_vm_types::{loaded_data::types::Type, values::Value}; use once_cell::sync::Lazy; use vm::{ file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldDefinitionIndex, FunctionHandleIndex, - StructDefinitionIndex, NO_TYPE_ACTUALS, NUMBER_OF_NATIVE_FUNCTIONS, + AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldHandleIndex, FieldInstantiationIndex, + FunctionHandleIndex, FunctionInstantiationIndex, StructDefInstantiationIndex, + StructDefinitionIndex, NUMBER_OF_NATIVE_FUNCTIONS, }, gas_schedule::{CostTable, GasCost, GAS_SCHEDULE_NAME, MAXIMUM_NUMBER_OF_GAS_UNITS}, }; @@ -20,12 +21,20 @@ static INITIAL_GAS_SCHEDULE: Lazy> = Lazy::new(|| { use Bytecode::*; let instrs = vec![ ( - MoveToSender(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MoveToSender(StructDefinitionIndex::new(0)), + GasCost::new(774, 1), + ), + ( + MoveToSenderGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(774, 1), ), (GetTxnSenderAddress, GasCost::new(30, 1)), ( - MoveFrom(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MoveFrom(StructDefinitionIndex::new(0)), + GasCost::new(917, 1), + ), + ( + MoveFromGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(917, 1), ), (BrTrue(0), GasCost::new(31, 1)), @@ -39,11 +48,19 @@ static INITIAL_GAS_SCHEDULE: Lazy> = Lazy::new(|| { (ReadRef, GasCost::new(51, 1)), (Sub, GasCost::new(44, 1)), ( - MutBorrowField(FieldDefinitionIndex::new(0)), + MutBorrowField(FieldHandleIndex::new(0)), + GasCost::new(58, 1), + ), + ( + MutBorrowFieldGeneric(FieldInstantiationIndex::new(0)), GasCost::new(58, 1), ), ( - ImmBorrowField(FieldDefinitionIndex::new(0)), + ImmBorrowField(FieldHandleIndex::new(0)), + GasCost::new(58, 1), + ), + ( + ImmBorrowFieldGeneric(FieldInstantiationIndex::new(0)), GasCost::new(58, 1), ), (Add, GasCost::new(45, 1)), @@ -67,14 +84,16 @@ static INITIAL_GAS_SCHEDULE: Lazy> = Lazy::new(|| { (Shr, GasCost::new(46, 1)), (Neq, GasCost::new(51, 1)), (Not, GasCost::new(35, 1)), + (Call(FunctionHandleIndex::new(0)), GasCost::new(197, 1)), ( - Call(FunctionHandleIndex::new(0), NO_TYPE_ACTUALS), + CallGeneric(FunctionInstantiationIndex::new(0)), GasCost::new(197, 1), ), (Le, GasCost::new(47, 1)), (Branch(0), GasCost::new(10, 1)), + (Unpack(StructDefinitionIndex::new(0)), GasCost::new(94, 1)), ( - Unpack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + UnpackGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(94, 1), ), (Or, GasCost::new(43, 1)), @@ -83,8 +102,9 @@ static INITIAL_GAS_SCHEDULE: Lazy> = Lazy::new(|| { (GetTxnGasUnitPrice, GasCost::new(29, 1)), (Mod, GasCost::new(42, 1)), (BrFalse(0), GasCost::new(29, 1)), + (Exists(StructDefinitionIndex::new(0)), GasCost::new(856, 1)), ( - Exists(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + ExistsGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(856, 1), ), (GetGasRemaining, GasCost::new(32, 1)), @@ -93,19 +113,28 @@ static INITIAL_GAS_SCHEDULE: Lazy> = Lazy::new(|| { (GetTxnSequenceNumber, GasCost::new(29, 1)), (FreezeRef, GasCost::new(10, 1)), ( - MutBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MutBorrowGlobal(StructDefinitionIndex::new(0)), + GasCost::new(929, 1), + ), + ( + MutBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), + GasCost::new(929, 1), + ), + ( + ImmBorrowGlobal(StructDefinitionIndex::new(0)), GasCost::new(929, 1), ), ( - ImmBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + ImmBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(929, 1), ), (Div, GasCost::new(41, 1)), (Eq, GasCost::new(48, 1)), (LdByteArray(ByteArrayPoolIndex::new(0)), GasCost::new(56, 1)), (Gt, GasCost::new(46, 1)), + (Pack(StructDefinitionIndex::new(0)), GasCost::new(73, 1)), ( - Pack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + PackGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(73, 1), ), ]; diff --git a/language/vm/src/access.rs b/language/vm/src/access.rs index 516cd39029d9..f8c3314aafe6 100644 --- a/language/vm/src/access.rs +++ b/language/vm/src/access.rs @@ -3,22 +3,8 @@ //! Defines accessors for compiled modules. -use crate::{ - file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, CompiledModule, CompiledModuleMut, CompiledScript, - FieldDefinition, FieldDefinitionIndex, FunctionDefinition, FunctionDefinitionIndex, - FunctionHandle, FunctionHandleIndex, FunctionSignature, FunctionSignatureIndex, - IdentifierIndex, LocalsSignature, LocalsSignatureIndex, MemberCount, ModuleHandle, - ModuleHandleIndex, StructDefinition, StructDefinitionIndex, StructHandle, - StructHandleIndex, TypeSignature, TypeSignatureIndex, - }, - internals::ModuleIndex, -}; -use libra_types::{ - account_address::AccountAddress, - language_storage::ModuleId, - vm_error::{StatusCode, VMStatus}, -}; +use crate::{file_format::*, internals::ModuleIndex}; +use libra_types::{account_address::AccountAddress, language_storage::ModuleId}; use move_core_types::identifier::{IdentStr, Identifier}; /// Represents accessors for a compiled module. @@ -30,9 +16,7 @@ pub trait ModuleAccess: Sync { /// Returns the `ModuleHandle` for `self`. fn self_handle(&self) -> &ModuleHandle { - self.module_handle_at(ModuleHandleIndex::new( - CompiledModule::IMPLEMENTED_MODULE_INDEX, - )) + self.module_handle_at(ModuleHandleIndex(CompiledModule::IMPLEMENTED_MODULE_INDEX)) } /// Returns the name of the module. @@ -57,16 +41,24 @@ pub trait ModuleAccess: Sync { &self.as_module().as_inner().function_handles[idx.into_index()] } - fn type_signature_at(&self, idx: TypeSignatureIndex) -> &TypeSignature { - &self.as_module().as_inner().type_signatures[idx.into_index()] + fn field_handle_at(&self, idx: FieldHandleIndex) -> &FieldHandle { + &self.as_module().as_inner().field_handles[idx.into_index()] } - fn function_signature_at(&self, idx: FunctionSignatureIndex) -> &FunctionSignature { - &self.as_module().as_inner().function_signatures[idx.into_index()] + fn struct_instantiation_at(&self, idx: StructDefInstantiationIndex) -> &StructDefInstantiation { + &self.as_module().as_inner().struct_def_instantiations[idx.into_index()] } - fn locals_signature_at(&self, idx: LocalsSignatureIndex) -> &LocalsSignature { - &self.as_module().as_inner().locals_signatures[idx.into_index()] + fn function_instantiation_at(&self, idx: FunctionInstantiationIndex) -> &FunctionInstantiation { + &self.as_module().as_inner().function_instantiations[idx.into_index()] + } + + fn field_instantiation_at(&self, idx: FieldInstantiationIndex) -> &FieldInstantiation { + &self.as_module().as_inner().field_instantiations[idx.into_index()] + } + + fn signature_at(&self, idx: SignatureIndex) -> &Signature { + &self.as_module().as_inner().signatures[idx.into_index()] } fn identifier_at(&self, idx: IdentifierIndex) -> &IdentStr { @@ -85,20 +77,10 @@ pub trait ModuleAccess: Sync { &self.as_module().as_inner().struct_defs[idx.into_index()] } - fn field_def_at(&self, idx: FieldDefinitionIndex) -> &FieldDefinition { - &self.as_module().as_inner().field_defs[idx.into_index()] - } - fn function_def_at(&self, idx: FunctionDefinitionIndex) -> &FunctionDefinition { &self.as_module().as_inner().function_defs[idx.into_index()] } - fn get_field_signature(&self, field_definition_index: FieldDefinitionIndex) -> &TypeSignature { - let field_definition = self.field_def_at(field_definition_index); - self.type_signature_at(field_definition.signature) - } - - // XXX is a partial range required here? fn module_handles(&self) -> &[ModuleHandle] { &self.as_module().as_inner().module_handles } @@ -111,16 +93,24 @@ pub trait ModuleAccess: Sync { &self.as_module().as_inner().function_handles } - fn type_signatures(&self) -> &[TypeSignature] { - &self.as_module().as_inner().type_signatures + fn field_handles(&self) -> &[FieldHandle] { + &self.as_module().as_inner().field_handles } - fn function_signatures(&self) -> &[FunctionSignature] { - &self.as_module().as_inner().function_signatures + fn struct_instantiations(&self) -> &[StructDefInstantiation] { + &self.as_module().as_inner().struct_def_instantiations } - fn locals_signatures(&self) -> &[LocalsSignature] { - &self.as_module().as_inner().locals_signatures + fn function_instantiations(&self) -> &[FunctionInstantiation] { + &self.as_module().as_inner().function_instantiations + } + + fn field_instantiations(&self) -> &[FieldInstantiation] { + &self.as_module().as_inner().field_instantiations + } + + fn signatures(&self) -> &[Signature] { + &self.as_module().as_inner().signatures } fn byte_array_pool(&self) -> &[Vec] { @@ -139,10 +129,6 @@ pub trait ModuleAccess: Sync { &self.as_module().as_inner().struct_defs } - fn field_defs(&self) -> &[FieldDefinition] { - &self.as_module().as_inner().field_defs - } - fn function_defs(&self) -> &[FunctionDefinition] { &self.as_module().as_inner().function_defs } @@ -154,28 +140,6 @@ pub trait ModuleAccess: Sync { fn self_id(&self) -> ModuleId { self.as_module().self_id() } - - fn field_def_range( - &self, - field_count: MemberCount, - first_field: FieldDefinitionIndex, - ) -> &[FieldDefinition] { - let first_field = first_field.0 as usize; - let field_count = field_count as usize; - // Both `first_field` and `field_count` are `u16` before being converted to usize - assume!(first_field <= usize::max_value() - field_count); - let last_field = first_field + field_count; - &self.as_module().as_inner().field_defs[first_field..last_field] - } - - fn is_field_in_struct( - &self, - field_definition_index: FieldDefinitionIndex, - struct_handle_index: StructHandleIndex, - ) -> bool { - let field_definition = self.field_def_at(field_definition_index); - struct_handle_index == field_definition.struct_ - } } /// Represents accessors for a compiled script. @@ -187,9 +151,7 @@ pub trait ScriptAccess: Sync { /// Returns the `ModuleHandle` for `self`. fn self_handle(&self) -> &ModuleHandle { - self.module_handle_at(ModuleHandleIndex::new( - CompiledModule::IMPLEMENTED_MODULE_INDEX, - )) + self.module_handle_at(ModuleHandleIndex(CompiledModule::IMPLEMENTED_MODULE_INDEX)) } fn module_handle_at(&self, idx: ModuleHandleIndex) -> &ModuleHandle { @@ -204,16 +166,8 @@ pub trait ScriptAccess: Sync { &self.as_script().as_inner().function_handles[idx.into_index()] } - fn type_signature_at(&self, idx: TypeSignatureIndex) -> &TypeSignature { - &self.as_script().as_inner().type_signatures[idx.into_index()] - } - - fn function_signature_at(&self, idx: FunctionSignatureIndex) -> &FunctionSignature { - &self.as_script().as_inner().function_signatures[idx.into_index()] - } - - fn locals_signature_at(&self, idx: LocalsSignatureIndex) -> &LocalsSignature { - &self.as_script().as_inner().locals_signatures[idx.into_index()] + fn signature_at(&self, idx: SignatureIndex) -> &Signature { + &self.as_script().as_inner().signatures[idx.into_index()] } fn identifier_at(&self, idx: IdentifierIndex) -> &IdentStr { @@ -228,6 +182,10 @@ pub trait ScriptAccess: Sync { &self.as_script().as_inner().address_pool[idx.into_index()] } + fn function_instantiation_at(&self, idx: FunctionInstantiationIndex) -> &FunctionInstantiation { + &self.as_script().as_inner().function_instantiations[idx.into_index()] + } + fn module_handles(&self) -> &[ModuleHandle] { &self.as_script().as_inner().module_handles } @@ -240,16 +198,12 @@ pub trait ScriptAccess: Sync { &self.as_script().as_inner().function_handles } - fn type_signatures(&self) -> &[TypeSignature] { - &self.as_script().as_inner().type_signatures - } - - fn function_signatures(&self) -> &[FunctionSignature] { - &self.as_script().as_inner().function_signatures + fn function_instantiations(&self) -> &[FunctionInstantiation] { + &self.as_script().as_inner().function_instantiations } - fn locals_signatures(&self) -> &[LocalsSignature] { - &self.as_script().as_inner().locals_signatures + fn signatures(&self) -> &[Signature] { + &self.as_script().as_inner().signatures } fn byte_array_pool(&self) -> &[Vec] { @@ -280,31 +234,3 @@ impl ScriptAccess for CompiledScript { self } } - -impl CompiledModuleMut { - #[inline] - pub(crate) fn check_field_range( - &self, - field_count: MemberCount, - first_field: FieldDefinitionIndex, - ) -> Option { - let first_field = first_field.into_index(); - let field_count = field_count as usize; - // Both first_field and field_count are u16 so this is guaranteed to not overflow. - // Note that last_field is exclusive, i.e. fields are in the range - // [first_field, last_field). - let last_field = first_field + field_count; - if last_field > self.field_defs.len() { - let msg = format!( - "Field definition range [{},{}) out of range for {}", - first_field, - last_field, - self.field_defs.len() - ); - let status = VMStatus::new(StatusCode::RANGE_OUT_OF_BOUNDS).with_message(msg); - Some(status) - } else { - None - } - } -} diff --git a/language/vm/src/check_bounds.rs b/language/vm/src/check_bounds.rs index 3aded8993119..0719beefbfd3 100644 --- a/language/vm/src/check_bounds.rs +++ b/language/vm/src/check_bounds.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - errors::{append_err_info, bounds_error, bytecode_offset_err, verification_error}, + errors::{bounds_error, bytecode_offset_err, verification_error}, file_format::{ - Bytecode, CodeUnit, CompiledModuleMut, FieldDefinition, FunctionDefinition, FunctionHandle, - FunctionSignature, Kind, LocalsSignature, LocalsSignatureIndex, ModuleHandle, - SignatureToken, StructDefinition, StructFieldInformation, StructHandle, TypeSignature, + Bytecode, CompiledModuleMut, FieldHandle, FieldInstantiation, FunctionDefinition, + FunctionHandle, FunctionInstantiation, ModuleHandle, Signature, SignatureToken, + StructDefInstantiation, StructDefinition, StructFieldInformation, StructHandle, }, internals::ModuleIndex, IndexKind, @@ -23,506 +23,477 @@ impl<'a> BoundsChecker<'a> { } pub fn verify(self) -> Vec { - let mut errors: Vec> = vec![]; + let mut errors: Vec = vec![]; - // A module (or script) must always have at least one module handle. (For modules the first - // handle should be the same as the sender -- the bytecode verifier is unaware of - // transactions so it does not perform this check. + // TODO: this will not be true once we change CompiledScript and remove the + // FunctionDefinition for `main` if self.module.module_handles.is_empty() { let status = verification_error(IndexKind::ModuleHandle, 0, StatusCode::NO_MODULE_HANDLES); - errors.push(vec![status]); + errors.push(status); } - errors.push(Self::verify_pool( - IndexKind::ModuleHandle, - self.module.module_handles.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::StructHandle, - self.module.struct_handles.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::FunctionHandle, - self.module.function_handles.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::StructDefinition, - self.module.struct_defs.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::FieldDefinition, - self.module.field_defs.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::FunctionDefinition, - self.module.function_defs.iter(), - self.module, - )); - errors.push(Self::verify_pool( - IndexKind::FunctionSignature, - self.module.function_signatures.iter(), - self.module, - )); - - // Check the struct handle indices in locals signatures. - // Type parameter indices are checked later in a separate pass. - errors.push( - self.module - .locals_signatures - .iter() - .enumerate() - .flat_map(|(idx, locals)| { - locals - .check_struct_handles(&self.module.struct_handles) - .into_iter() - .map(move |err| append_err_info(err, IndexKind::LocalsSignature, idx)) - }) - .collect(), - ); - - // Check the struct handle indices in type signatures. - errors.push( - self.module - .type_signatures - .iter() - .enumerate() - .flat_map(|(idx, ty)| { - ty.check_struct_handles(&self.module.struct_handles) - .into_iter() - .map(move |err| append_err_info(err, IndexKind::TypeSignature, idx)) - }) - .collect(), - ); - - let errors: Vec<_> = errors.into_iter().flatten().collect(); - if !errors.is_empty() { - return errors; + for signature in &self.module.signatures { + self.check_signature(signature, &mut errors); } - - // Fields and function bodies need to be done once the rest of the module is validated. - let errors_type_signatures = self.module.field_defs.iter().flat_map(|field_def| { - let sh = &self.module.struct_handles[field_def.struct_.0 as usize]; - let sig = &self.module.type_signatures[field_def.signature.0 as usize]; - - sig.check_type_parameters(sh.type_formals.len()) - }); - - let errors_code_units = self.module.function_defs.iter().flat_map(|function_def| { - if function_def.is_native() { - vec![] - } else { - let fh = &self.module.function_handles[function_def.function.0 as usize]; - let sig = &self.module.function_signatures[fh.signature.0 as usize]; - function_def.code.check_bounds((self.module, sig)) - } - }); - - errors_type_signatures.chain(errors_code_units).collect() - } - - #[inline] - fn verify_pool( - kind: IndexKind, - iter: impl Iterator, - context: Context, - ) -> Vec - where - Context: Copy, - Item: BoundsCheck, - { - iter.enumerate() - .flat_map(move |(idx, elem)| { - elem.check_bounds(context) - .into_iter() - .map(move |err| append_err_info(err, kind, idx)) - }) - .collect() - } -} - -pub trait BoundsCheck { - fn check_bounds(&self, context: Context) -> Vec; -} - -#[inline] -fn check_bounds_impl(pool: &[T], idx: I) -> Option -where - I: ModuleIndex, -{ - let idx = idx.into_index(); - let len = pool.len(); - if idx >= len { - let status = bounds_error(I::KIND, idx, len, StatusCode::INDEX_OUT_OF_BOUNDS); - Some(status) - } else { - None - } -} - -#[inline] -fn check_code_unit_bounds_impl(pool: &[T], bytecode_offset: usize, idx: I) -> Vec -where - I: ModuleIndex, -{ - let idx = idx.into_index(); - let len = pool.len(); - if idx >= len { - let status = bytecode_offset_err( - I::KIND, - idx, - len, - bytecode_offset, - StatusCode::INDEX_OUT_OF_BOUNDS, - ); - vec![status] - } else { - vec![] + for module_handle in &self.module.module_handles { + self.check_module_handle(module_handle, &mut errors); + } + for struct_handle in &self.module.struct_handles { + self.check_struct_handle(struct_handle, &mut errors); + } + for function_handle in &self.module.function_handles { + self.check_function_handle(function_handle, &mut errors); + } + for field_handle in &self.module.field_handles { + self.check_field_handle(field_handle, &mut errors); + } + for struct_instantiation in &self.module.struct_def_instantiations { + self.check_struct_instantiation(struct_instantiation, &mut errors); + } + for function_instantiation in &self.module.function_instantiations { + self.check_function_instantiation(function_instantiation, &mut errors); + } + for field_instantiation in &self.module.field_instantiations { + self.check_field_instantiation(field_instantiation, &mut errors); + } + for struct_def in &self.module.struct_defs { + self.check_struct_def(struct_def, &mut errors); + } + for function_def in &self.module.function_defs { + self.check_function_def(function_def, &mut errors); + } + errors } -} -impl BoundsCheck<&CompiledModuleMut> for ModuleHandle { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.address_pool, self.address), - check_bounds_impl(&module.identifiers, self.name), - ] - .into_iter() - .flatten() - .collect() + fn check_module_handle(&self, module_handle: &ModuleHandle, errors: &mut Vec) { + check_bounds_impl(&self.module.address_pool, module_handle.address, errors); + check_bounds_impl(&self.module.identifiers, module_handle.name, errors); } -} -impl BoundsCheck<&CompiledModuleMut> for StructHandle { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.module_handles, self.module), - check_bounds_impl(&module.identifiers, self.name), - ] - .into_iter() - .flatten() - .collect() + fn check_struct_handle(&self, struct_handle: &StructHandle, errors: &mut Vec) { + check_bounds_impl(&self.module.module_handles, struct_handle.module, errors); + check_bounds_impl(&self.module.identifiers, struct_handle.name, errors); } -} -impl BoundsCheck<&CompiledModuleMut> for FunctionHandle { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.module_handles, self.module), - check_bounds_impl(&module.identifiers, self.name), - check_bounds_impl(&module.function_signatures, self.signature), - ] - .into_iter() - .flatten() - .collect() + fn check_function_handle(&self, function_handle: &FunctionHandle, errors: &mut Vec) { + check_bounds_impl(&self.module.module_handles, function_handle.module, errors); + check_bounds_impl(&self.module.identifiers, function_handle.name, errors); + check_bounds_impl(&self.module.signatures, function_handle.parameters, errors); + check_bounds_impl(&self.module.signatures, function_handle.return_, errors); + // function signature type paramters must be in bounds to the function type parameters + let type_param_count = function_handle.type_parameters.len(); + if let Some(sig) = self + .module + .signatures + .get(function_handle.parameters.into_index()) + { + for ty in &sig.0 { + self.check_type_parameter(ty, type_param_count, errors); + } + } + if let Some(sig) = self + .module + .signatures + .get(function_handle.return_.into_index()) + { + for ty in &sig.0 { + self.check_type_parameter(ty, type_param_count, errors); + } + } } -} -impl BoundsCheck<&CompiledModuleMut> for StructDefinition { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.struct_handles, self.struct_handle), - match &self.field_information { - StructFieldInformation::Native => None, - StructFieldInformation::Declared { - field_count, - fields, - } => module.check_field_range(*field_count, *fields), - }, - ] - .into_iter() - .flatten() - .collect() + fn check_field_handle(&self, field_handle: &FieldHandle, errors: &mut Vec) { + check_bounds_impl(&self.module.struct_defs, field_handle.owner, errors); + // field offset must be in bounds, struct def just checked above must exist + if let Some(struct_def) = &self.module.struct_defs.get(field_handle.owner.into_index()) { + let fields_count = match &struct_def.field_information { + StructFieldInformation::Native => 0, + StructFieldInformation::Declared(fields) => fields.len(), + }; + if field_handle.field as usize >= fields_count { + errors.push(bounds_error( + IndexKind::MemberCount, + field_handle.field as usize, + fields_count, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); + } + } } -} -impl BoundsCheck<&CompiledModuleMut> for FieldDefinition { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.struct_handles, self.struct_), - check_bounds_impl(&module.identifiers, self.name), - check_bounds_impl(&module.type_signatures, self.signature), - ] - .into_iter() - .flatten() - .collect() + fn check_struct_instantiation( + &self, + struct_instantiation: &StructDefInstantiation, + errors: &mut Vec, + ) { + check_bounds_impl(&self.module.struct_defs, struct_instantiation.def, errors); + check_bounds_impl( + &self.module.signatures, + struct_instantiation.type_parameters, + errors, + ); } -} -impl BoundsCheck<&CompiledModuleMut> for FunctionDefinition { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - vec![ - check_bounds_impl(&module.function_handles, self.function), - if self.is_native() { - None - } else { - check_bounds_impl(&module.locals_signatures, self.code.locals) - }, - ] - .into_iter() - .flatten() - .chain( - self.acquires_global_resources - .iter() - .flat_map(|idx| check_bounds_impl(&module.struct_defs, *idx)), - ) - .collect() + fn check_function_instantiation( + &self, + function_instantiation: &FunctionInstantiation, + errors: &mut Vec, + ) { + check_bounds_impl( + &self.module.function_handles, + function_instantiation.handle, + errors, + ); + check_bounds_impl( + &self.module.signatures, + function_instantiation.type_parameters, + errors, + ); } -} -impl BoundsCheck for TypeSignature -where - Context: Copy, - SignatureToken: BoundsCheck, -{ - #[inline] - fn check_bounds(&self, context: Context) -> Vec { - self.0.check_bounds(context).into_iter().collect() + fn check_field_instantiation( + &self, + field_instantiation: &FieldInstantiation, + errors: &mut Vec, + ) { + check_bounds_impl( + &self.module.field_handles, + field_instantiation.handle, + errors, + ); + check_bounds_impl( + &self.module.signatures, + field_instantiation.type_parameters, + errors, + ); } -} -impl TypeSignature { - fn check_struct_handles(&self, struct_handles: &[StructHandle]) -> Vec { - self.0.check_struct_handles(struct_handles) + fn check_signature(&self, signature: &Signature, errors: &mut Vec) { + for ty in &signature.0 { + self.check_type(ty, errors); + } } - fn check_type_parameters(&self, type_formals_len: usize) -> Vec { - self.0.check_type_parameters(type_formals_len) + fn check_struct_def(&self, struct_def: &StructDefinition, errors: &mut Vec) { + check_bounds_impl( + &self.module.struct_handles, + struct_def.struct_handle, + errors, + ); + // check signature (type) and type parameter for the field type + if let StructFieldInformation::Declared(fields) = &struct_def.field_information { + let type_param_count = match self + .module + .struct_handles + .get(struct_def.struct_handle.into_index()) + { + Some(sh) => sh.type_parameters.len(), + None => 0, + }; + // field signatures are inlined + for field in fields { + check_bounds_impl(&self.module.identifiers, field.name, errors); + self.check_type(&field.signature.0, errors); + self.check_type_parameter(&field.signature.0, type_param_count, errors); + } + } } -} -impl BoundsCheck<&CompiledModuleMut> for FunctionSignature { - #[inline] - fn check_bounds(&self, module: &CompiledModuleMut) -> Vec { - self.return_types - .iter() - .map(|token| token.check_bounds((module, self))) - .chain( - self.arg_types - .iter() - .map(|token| token.check_bounds((module, self))), - ) - .flatten() - .collect() + fn check_function_def(&self, function_def: &FunctionDefinition, errors: &mut Vec) { + check_bounds_impl(&self.module.function_handles, function_def.function, errors); + for ty in &function_def.acquires_global_resources { + check_bounds_impl(&self.module.struct_defs, *ty, errors); + } + self.check_code(function_def, errors); } -} -impl LocalsSignature { - fn check_type_parameters(&self, type_formals_len: usize) -> Vec { - self.0 - .iter() - .flat_map(|ty| ty.check_type_parameters(type_formals_len)) - .collect() - } + fn check_code(&self, function_def: &FunctionDefinition, errors: &mut Vec) { + if function_def.is_native() { + return; + } + let code_unit = &function_def.code; + check_bounds_impl(&self.module.signatures, code_unit.locals, errors); + + let type_param_count = match self + .module + .function_handles + .get(function_def.function.into_index()) + { + Some(fh) => fh.type_parameters.len(), + None => 0, + }; + // if there are locals check that the type parameters in local signature are in bounds. + let locals = match &self.module.signatures.get(code_unit.locals.into_index()) { + Some(locals) => &locals.0, + None => return, // stop now + }; + let locals_count = locals.len(); + for local in locals { + self.check_type_parameter(local, type_param_count, errors); + } - fn check_struct_handles(&self, struct_handles: &[StructHandle]) -> Vec { - self.0 - .iter() - .flat_map(|ty| ty.check_struct_handles(struct_handles)) - .collect() - } -} + // check bytecodes + let code_len = code_unit.code.len(); + for (bytecode_offset, bytecode) in code_unit.code.iter().enumerate() { + use self::Bytecode::*; + + match bytecode { + LdAddr(idx) => { + check_code_unit_bounds_impl( + &self.module.address_pool, + bytecode_offset, + *idx, + errors, + ); + } + LdByteArray(idx) => { + check_code_unit_bounds_impl( + &self.module.byte_array_pool, + bytecode_offset, + *idx, + errors, + ); + } + MutBorrowField(idx) | ImmBorrowField(idx) => { + check_code_unit_bounds_impl( + &self.module.field_handles, + bytecode_offset, + *idx, + errors, + ); + } + MutBorrowFieldGeneric(idx) | ImmBorrowFieldGeneric(idx) => { + check_code_unit_bounds_impl( + &self.module.field_instantiations, + bytecode_offset, + *idx, + errors, + ); + // check type parameters in borrow are bound to the function type parameters + if let Some(field_inst) = self.module.field_instantiations.get(idx.into_index()) + { + if let Some(sig) = self + .module + .signatures + .get(field_inst.type_parameters.into_index()) + { + for ty in &sig.0 { + self.check_type_parameter(ty, type_param_count, errors); + } + } + } + } + Call(idx) => { + check_code_unit_bounds_impl( + &self.module.function_handles, + bytecode_offset, + *idx, + errors, + ); + } + CallGeneric(idx) => { + check_code_unit_bounds_impl( + &self.module.function_instantiations, + bytecode_offset, + *idx, + errors, + ); + // check type parameters in call are bound to the function type parameters + if let Some(func_inst) = + self.module.function_instantiations.get(idx.into_index()) + { + if let Some(sig) = self + .module + .signatures + .get(func_inst.type_parameters.into_index()) + { + for ty in &sig.0 { + self.check_type_parameter(ty, type_param_count, errors); + } + } + } + } + Pack(idx) | Unpack(idx) | Exists(idx) | ImmBorrowGlobal(idx) + | MutBorrowGlobal(idx) | MoveFrom(idx) | MoveToSender(idx) => { + check_code_unit_bounds_impl( + &self.module.struct_defs, + bytecode_offset, + *idx, + errors, + ); + } + PackGeneric(idx) + | UnpackGeneric(idx) + | ExistsGeneric(idx) + | ImmBorrowGlobalGeneric(idx) + | MutBorrowGlobalGeneric(idx) + | MoveFromGeneric(idx) + | MoveToSenderGeneric(idx) => { + check_code_unit_bounds_impl( + &self.module.struct_def_instantiations, + bytecode_offset, + *idx, + errors, + ); + // check type parameters in type operations are bound to the function type parameters + if let Some(struct_inst) = + self.module.struct_def_instantiations.get(idx.into_index()) + { + if let Some(sig) = self + .module + .signatures + .get(struct_inst.type_parameters.into_index()) + { + for ty in &sig.0 { + self.check_type_parameter(ty, type_param_count, errors); + } + } + } + } + // Instructions that refer to this code block. + BrTrue(offset) | BrFalse(offset) | Branch(offset) => { + let offset = *offset as usize; + if offset >= code_len { + errors.push(bytecode_offset_err( + IndexKind::CodeDefinition, + offset, + code_len, + bytecode_offset, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); + } + } + // Instructions that refer to the locals. + CopyLoc(idx) | MoveLoc(idx) | StLoc(idx) | MutBorrowLoc(idx) + | ImmBorrowLoc(idx) => { + let idx = *idx as usize; + if idx >= locals_count { + errors.push(bytecode_offset_err( + IndexKind::LocalPool, + idx, + locals_count, + bytecode_offset, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); + } + } -impl BoundsCheck<(&[StructHandle], usize)> for SignatureToken { - fn check_bounds(&self, context: (&[StructHandle], usize)) -> Vec { - self.check_type_parameters(context.1) - .into_iter() - .chain(self.check_struct_handles(context.0)) - .collect() + // List out the other options explicitly so there's a compile error if a new + // bytecode gets added. + FreezeRef | Pop | Ret | LdU8(_) | LdU64(_) | LdU128(_) | CastU8 | CastU64 + | CastU128 | LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub | Mul | Mod + | Div | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt + | Le | Ge | Abort | GetTxnGasUnitPrice | GetTxnMaxGasUnits | GetGasRemaining + | GetTxnSenderAddress | GetTxnSequenceNumber | GetTxnPublicKey => (), + } + } } -} -impl SignatureToken { - pub fn check_type_parameters(&self, type_formals_len: usize) -> Vec { - match self { - SignatureToken::Struct(_, type_actuals) => type_actuals - .iter() - .flat_map(|ty| ty.check_type_parameters(type_formals_len)) - .collect(), - SignatureToken::Reference(ty) | SignatureToken::MutableReference(ty) => { - ty.check_type_parameters(type_formals_len) + fn check_type(&self, type_: &SignatureToken, errors: &mut Vec) { + use self::SignatureToken::*; + + match type_ { + Bool | U8 | U64 | U128 | Address | TypeParameter(_) => (), + Reference(ty) | MutableReference(ty) | Vector(ty) => self.check_type(ty, errors), + Struct(idx) => { + check_bounds_impl(&self.module.struct_handles, *idx, errors); + if let Some(sh) = self.module.struct_handles.get(idx.into_index()) { + if !sh.type_parameters.is_empty() { + errors.push( + VMStatus::new(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH) + .with_message(format!( + "expected {} type parameters got 0 (Struct)", + sh.type_parameters.len(), + )), + ); + } + } } - SignatureToken::TypeParameter(idx) => { - let idx = *idx as usize; - if idx >= type_formals_len { - vec![bounds_error( - IndexKind::TypeParameter, - idx, - type_formals_len, - StatusCode::INDEX_OUT_OF_BOUNDS, - )] - } else { - vec![] + StructInstantiation(idx, type_params) => { + check_bounds_impl(&self.module.struct_handles, *idx, errors); + if let Some(sh) = self.module.struct_handles.get(idx.into_index()) { + if sh.type_parameters.len() != type_params.len() { + errors.push( + VMStatus::new(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH) + .with_message(format!( + "expected {} type parameters got {}", + sh.type_parameters.len(), + type_params.len(), + )), + ); + } } } - _ => vec![], } } - pub fn check_struct_handles(&self, struct_handles: &[StructHandle]) -> Vec { - match self { - SignatureToken::Struct(idx, type_actuals) => { - let mut errors: Vec<_> = type_actuals - .iter() - .flat_map(|ty| ty.check_struct_handles(struct_handles)) - .collect(); - if let Some(err) = check_bounds_impl(struct_handles, *idx) { - errors.push(err); + fn check_type_parameter( + &self, + type_: &SignatureToken, + type_param_count: usize, + errors: &mut Vec, + ) { + use self::SignatureToken::*; + + match type_ { + Bool | U8 | U64 | U128 | Address | Struct(_) => (), + Reference(ty) | MutableReference(ty) | Vector(ty) => { + self.check_type_parameter(ty, type_param_count, errors) + } + // TODO: is this correct? + SignatureToken::StructInstantiation(_, type_params) => { + for type_param in type_params { + self.check_type_parameter(type_param, type_param_count, errors); } - errors } - SignatureToken::Reference(ty) | SignatureToken::MutableReference(ty) => { - ty.check_struct_handles(struct_handles) + SignatureToken::TypeParameter(idx) => { + if *idx as usize >= type_param_count { + errors.push(bounds_error( + IndexKind::TypeParameter, + *idx as usize, + type_param_count, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); + } } - _ => vec![], } } } -impl BoundsCheck<(&[StructHandle], &[Kind])> for SignatureToken { - fn check_bounds(&self, context: (&[StructHandle], &[Kind])) -> Vec { - self.check_bounds((context.0, context.1.len())) - } -} - -impl BoundsCheck<(&CompiledModuleMut, &FunctionSignature)> for SignatureToken { - fn check_bounds(&self, context: (&CompiledModuleMut, &FunctionSignature)) -> Vec { - self.check_bounds(( - context.0.struct_handles.as_slice(), - context.1.type_formals.as_slice(), - )) - } -} - -impl BoundsCheck<(&CompiledModuleMut, &StructHandle)> for SignatureToken { - fn check_bounds(&self, context: (&CompiledModuleMut, &StructHandle)) -> Vec { - self.check_bounds(( - context.0.struct_handles.as_slice(), - context.1.type_formals.as_slice(), - )) +fn check_bounds_impl(pool: &[T], idx: I, errors: &mut Vec) +where + I: ModuleIndex, +{ + let idx = idx.into_index(); + let len = pool.len(); + if idx >= len { + errors.push(bounds_error( + I::KIND, + idx, + len, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); } } -fn check_type_actuals_bounds( - context: (&CompiledModuleMut, &FunctionSignature), +fn check_code_unit_bounds_impl( + pool: &[T], bytecode_offset: usize, - idx: LocalsSignatureIndex, -) -> Vec { - let (module, function_sig) = context; - let errs = check_code_unit_bounds_impl(&module.locals_signatures, bytecode_offset, idx); - if !errs.is_empty() { - return errs; - } - module.locals_signatures[idx.0 as usize].check_type_parameters(function_sig.type_formals.len()) -} - -impl BoundsCheck<(&CompiledModuleMut, &FunctionSignature)> for CodeUnit { - fn check_bounds(&self, context: (&CompiledModuleMut, &FunctionSignature)) -> Vec { - let (module, _) = context; - - let locals = &module.locals_signatures[self.locals.0 as usize]; - let locals_len = locals.0.len(); - - let code = &self.code; - let code_len = code.len(); - - code.iter() - .enumerate() - .flat_map(|(bytecode_offset, bytecode)| { - use self::Bytecode::*; - - match bytecode { - // Instructions that refer to other pools. - LdAddr(idx) => { - check_code_unit_bounds_impl(&module.address_pool, bytecode_offset, *idx) - } - LdByteArray(idx) => { - check_code_unit_bounds_impl(&module.byte_array_pool, bytecode_offset, *idx) - } - MutBorrowField(idx) | ImmBorrowField(idx) => { - check_code_unit_bounds_impl(&module.field_defs, bytecode_offset, *idx) - } - Call(idx, type_actuals_idx) => { - check_code_unit_bounds_impl(&module.function_handles, bytecode_offset, *idx) - .into_iter() - .chain(check_type_actuals_bounds( - context, - bytecode_offset, - *type_actuals_idx, - )) - .collect() - } - Pack(idx, type_actuals_idx) - | Unpack(idx, type_actuals_idx) - | Exists(idx, type_actuals_idx) - | ImmBorrowGlobal(idx, type_actuals_idx) - | MutBorrowGlobal(idx, type_actuals_idx) - | MoveFrom(idx, type_actuals_idx) - | MoveToSender(idx, type_actuals_idx) => { - check_code_unit_bounds_impl(&module.struct_defs, bytecode_offset, *idx) - .into_iter() - .chain(check_type_actuals_bounds( - context, - bytecode_offset, - *type_actuals_idx, - )) - .collect() - } - // Instructions that refer to this code block. - BrTrue(offset) | BrFalse(offset) | Branch(offset) => { - let offset = *offset as usize; - if offset >= code_len { - let status = bytecode_offset_err( - IndexKind::CodeDefinition, - offset, - code_len, - bytecode_offset, - StatusCode::INDEX_OUT_OF_BOUNDS, - ); - vec![status] - } else { - vec![] - } - } - // Instructions that refer to the locals. - CopyLoc(idx) | MoveLoc(idx) | StLoc(idx) | MutBorrowLoc(idx) - | ImmBorrowLoc(idx) => { - let idx = *idx as usize; - if idx >= locals_len { - let status = bytecode_offset_err( - IndexKind::LocalPool, - idx, - locals_len, - bytecode_offset, - StatusCode::INDEX_OUT_OF_BOUNDS, - ); - vec![status] - } else { - vec![] - } - } - - // List out the other options explicitly so there's a compile error if a new - // bytecode gets added. - FreezeRef | Pop | Ret | LdU8(_) | LdU64(_) | LdU128(_) | CastU8 | CastU64 - | CastU128 | LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub | Mul | Mod - | Div | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt - | Gt | Le | Ge | Abort | GetTxnGasUnitPrice | GetTxnMaxGasUnits - | GetGasRemaining | GetTxnSenderAddress | GetTxnSequenceNumber - | GetTxnPublicKey => vec![], - } - }) - .collect() + idx: I, + errors: &mut Vec, +) where + I: ModuleIndex, +{ + let idx = idx.into_index(); + let len = pool.len(); + if idx >= len { + errors.push(bytecode_offset_err( + I::KIND, + idx, + len, + bytecode_offset, + StatusCode::INDEX_OUT_OF_BOUNDS, + )); } } diff --git a/language/vm/src/deserializer.rs b/language/vm/src/deserializer.rs index e0004b5d5130..6e517ad78938 100644 --- a/language/vm/src/deserializer.rs +++ b/language/vm/src/deserializer.rs @@ -98,13 +98,13 @@ fn deserialize_compiled_module(binary: &[u8]) -> BinaryLoaderResult) -> BinaryLoaderResult { let mut magic = [0u8; BinaryConstants::LIBRA_MAGIC_SIZE]; if let Ok(count) = cursor.read(&mut magic) { - if count != BinaryConstants::LIBRA_MAGIC_SIZE { - return Err(VMStatus::new(StatusCode::MALFORMED)); - } else if magic != BinaryConstants::LIBRA_MAGIC { + if count != BinaryConstants::LIBRA_MAGIC_SIZE || magic != BinaryConstants::LIBRA_MAGIC { return Err(VMStatus::new(StatusCode::BAD_MAGIC)); } } else { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err( + VMStatus::new(StatusCode::MALFORMED).with_message("Bad binary header".to_string()) + ); } let major_ver = 1u8; let minor_ver = 0u8; @@ -113,18 +113,22 @@ fn check_binary(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { return Err(VMStatus::new(StatusCode::UNKNOWN_VERSION)); } } else { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err( + VMStatus::new(StatusCode::MALFORMED).with_message("Bad binary header".to_string()) + ); } if let Ok(ver) = cursor.read_u8() { if ver != minor_ver { return Err(VMStatus::new(StatusCode::UNKNOWN_VERSION)); } } else { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err( + VMStatus::new(StatusCode::MALFORMED).with_message("Bad binary header".to_string()) + ); } - cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Bad binary header".to_string()) + }) } /// Reads all the table headers. @@ -149,7 +153,7 @@ fn read_table(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { let count = read_u32_internal(cursor)?; Ok(Table::new(TableType::from_u8(kind)?, table_offset, count)) } else { - Err(VMStatus::new(StatusCode::MALFORMED)) + Err(VMStatus::new(StatusCode::MALFORMED).with_message("Error reading table".to_string())) } } @@ -195,11 +199,8 @@ trait CommonTables { fn get_module_handles(&mut self) -> &mut Vec; fn get_struct_handles(&mut self) -> &mut Vec; fn get_function_handles(&mut self) -> &mut Vec; - - fn get_type_signatures(&mut self) -> &mut TypeSignaturePool; - fn get_function_signatures(&mut self) -> &mut FunctionSignaturePool; - fn get_locals_signatures(&mut self) -> &mut LocalsSignaturePool; - + fn get_function_instantiations(&mut self) -> &mut Vec; + fn get_signatures(&mut self) -> &mut SignaturePool; fn get_identifiers(&mut self) -> &mut IdentifierPool; fn get_byte_array_pool(&mut self) -> &mut ByteArrayPool; fn get_address_pool(&mut self) -> &mut AddressPool; @@ -218,16 +219,12 @@ impl CommonTables for CompiledScriptMut { &mut self.function_handles } - fn get_type_signatures(&mut self) -> &mut TypeSignaturePool { - &mut self.type_signatures - } - - fn get_function_signatures(&mut self) -> &mut FunctionSignaturePool { - &mut self.function_signatures + fn get_function_instantiations(&mut self) -> &mut Vec { + &mut self.function_instantiations } - fn get_locals_signatures(&mut self) -> &mut LocalsSignaturePool { - &mut self.locals_signatures + fn get_signatures(&mut self) -> &mut SignaturePool { + &mut self.signatures } fn get_identifiers(&mut self) -> &mut IdentifierPool { @@ -256,16 +253,12 @@ impl CommonTables for CompiledModuleMut { &mut self.function_handles } - fn get_type_signatures(&mut self) -> &mut TypeSignaturePool { - &mut self.type_signatures + fn get_function_instantiations(&mut self) -> &mut Vec { + &mut self.function_instantiations } - fn get_function_signatures(&mut self) -> &mut FunctionSignaturePool { - &mut self.function_signatures - } - - fn get_locals_signatures(&mut self) -> &mut LocalsSignaturePool { - &mut self.locals_signatures + fn get_signatures(&mut self) -> &mut SignaturePool { + &mut self.signatures } fn get_identifiers(&mut self) -> &mut IdentifierPool { @@ -314,6 +307,12 @@ fn build_common_tables( TableType::FUNCTION_HANDLES => { load_function_handles(binary, table, common.get_function_handles())?; } + TableType::FUNCTION_INST => { + load_function_instantiations(binary, table, common.get_function_instantiations())?; + } + TableType::SIGNATURES => { + load_signatures(binary, table, common.get_signatures())?; + } TableType::ADDRESS_POOL => { load_address_pool(binary, table, common.get_address_pool())?; } @@ -323,18 +322,11 @@ fn build_common_tables( TableType::BYTE_ARRAY_POOL => { load_byte_array_pool(binary, table, common.get_byte_array_pool())?; } - TableType::TYPE_SIGNATURES => { - load_type_signatures(binary, table, common.get_type_signatures())?; - } - TableType::FUNCTION_SIGNATURES => { - load_function_signatures(binary, table, common.get_function_signatures())?; - } - TableType::LOCALS_SIGNATURES => { - load_locals_signatures(binary, table, common.get_locals_signatures())?; - } TableType::FUNCTION_DEFS - | TableType::FIELD_DEFS | TableType::STRUCT_DEFS + | TableType::STRUCT_DEF_INST + | TableType::FIELD_HANDLE + | TableType::FIELD_INST | TableType::MAIN => continue, } } @@ -352,24 +344,32 @@ fn build_module_tables( TableType::STRUCT_DEFS => { load_struct_defs(binary, table, &mut module.struct_defs)?; } - TableType::FIELD_DEFS => { - load_field_defs(binary, table, &mut module.field_defs)?; + TableType::STRUCT_DEF_INST => { + load_struct_instantiations(binary, table, &mut module.struct_def_instantiations)?; } TableType::FUNCTION_DEFS => { load_function_defs(binary, table, &mut module.function_defs)?; } + TableType::FIELD_HANDLE => { + load_field_handles(binary, table, &mut module.field_handles)?; + } + TableType::FIELD_INST => { + load_field_instantiations(binary, table, &mut module.field_instantiations)?; + } TableType::MODULE_HANDLES | TableType::STRUCT_HANDLES | TableType::FUNCTION_HANDLES + | TableType::FUNCTION_INST | TableType::ADDRESS_POOL | TableType::IDENTIFIERS | TableType::BYTE_ARRAY_POOL - | TableType::TYPE_SIGNATURES - | TableType::FUNCTION_SIGNATURES - | TableType::LOCALS_SIGNATURES => { + | TableType::SIGNATURES => { continue; } - TableType::MAIN => return Err(VMStatus::new(StatusCode::MALFORMED)), + TableType::MAIN => { + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad table in Module".to_string())) + } } } Ok(()) @@ -395,16 +395,20 @@ fn build_script_tables( TableType::MODULE_HANDLES | TableType::STRUCT_HANDLES | TableType::FUNCTION_HANDLES + | TableType::FUNCTION_INST + | TableType::SIGNATURES | TableType::ADDRESS_POOL | TableType::IDENTIFIERS - | TableType::BYTE_ARRAY_POOL - | TableType::TYPE_SIGNATURES - | TableType::FUNCTION_SIGNATURES - | TableType::LOCALS_SIGNATURES => { + | TableType::BYTE_ARRAY_POOL => { continue; } - TableType::STRUCT_DEFS | TableType::FIELD_DEFS | TableType::FUNCTION_DEFS => { - return Err(VMStatus::new(StatusCode::MALFORMED)); + TableType::STRUCT_DEFS + | TableType::STRUCT_DEF_INST + | TableType::FUNCTION_DEFS + | TableType::FIELD_INST + | TableType::FIELD_HANDLE => { + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad table in Script".to_string())); } } } @@ -450,12 +454,12 @@ fn load_struct_handles( let module_handle = read_uleb_u16_internal(&mut cursor)?; let name = read_uleb_u16_internal(&mut cursor)?; let is_nominal_resource = load_nominal_resource_flag(&mut cursor)?; - let type_formals = load_kinds(&mut cursor)?; + let type_parameters = load_kinds(&mut cursor)?; struct_handles.push(StructHandle { module: ModuleHandleIndex(module_handle), name: IdentifierIndex(name), is_nominal_resource, - type_formals, + type_parameters, }); } Ok(()) @@ -476,11 +480,62 @@ fn load_function_handles( } let module_handle = read_uleb_u16_internal(&mut cursor)?; let name = read_uleb_u16_internal(&mut cursor)?; - let signature = read_uleb_u16_internal(&mut cursor)?; + let parameters = read_uleb_u16_internal(&mut cursor)?; + let return_ = read_uleb_u16_internal(&mut cursor)?; + let type_parameters = load_kinds(&mut cursor)?; + function_handles.push(FunctionHandle { module: ModuleHandleIndex(module_handle), name: IdentifierIndex(name), - signature: FunctionSignatureIndex(signature), + parameters: SignatureIndex(parameters), + return_: SignatureIndex(return_), + type_parameters, + }); + } + Ok(()) +} + +/// Builds the `StructInstantiation` table. +fn load_struct_instantiations( + binary: &[u8], + table: &Table, + struct_insts: &mut Vec, +) -> BinaryLoaderResult<()> { + let start = table.offset as usize; + let end = start + table.count as usize; + let mut cursor = Cursor::new(&binary[start..end]); + loop { + if cursor.position() == u64::from(table.count) { + break; + } + let def = read_uleb_u16_internal(&mut cursor)?; + let type_parameters = read_uleb_u16_internal(&mut cursor)?; + struct_insts.push(StructDefInstantiation { + def: StructDefinitionIndex(def), + type_parameters: SignatureIndex(type_parameters), + }); + } + Ok(()) +} + +/// Builds the `FunctionInstantiation` table. +fn load_function_instantiations( + binary: &[u8], + table: &Table, + func_insts: &mut Vec, +) -> BinaryLoaderResult<()> { + let start = table.offset as usize; + let end = start + table.count as usize; + let mut cursor = Cursor::new(&binary[start..end]); + loop { + if cursor.position() == u64::from(table.count) { + break; + } + let handle = read_uleb_u16_internal(&mut cursor)?; + let type_parameters = read_uleb_u16_internal(&mut cursor)?; + func_insts.push(FunctionInstantiation { + handle: FunctionHandleIndex(handle), + type_parameters: SignatureIndex(type_parameters), }); } Ok(()) @@ -494,13 +549,16 @@ fn load_address_pool( ) -> BinaryLoaderResult<()> { let mut start = table.offset as usize; if table.count as usize % AccountAddress::LENGTH != 0 { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err( + VMStatus::new(StatusCode::MALFORMED).with_message("Bad Address pool size".to_string()) + ); } for _i in 0..table.count as usize / AccountAddress::LENGTH { let end_addr = start + AccountAddress::LENGTH; let address = (&binary[start..end_addr]).try_into(); if address.is_err() { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Invalid Address format".to_string())); } start = end_addr; @@ -521,15 +579,18 @@ fn load_identifiers( while cursor.position() < u64::from(table.count) { let size = read_uleb_u32_internal(&mut cursor)? as usize; if size > std::u16::MAX as usize { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad Identifier pool size".to_string())); } let mut buffer: Vec = vec![0u8; size]; if let Ok(count) = cursor.read(&mut buffer) { if count != size { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad Identifier pool size".to_string())); } - let s = - Identifier::from_utf8(buffer).map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let s = Identifier::from_utf8(buffer).map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Invalid Identifier".to_string()) + })?; identifiers.push(s); } @@ -549,12 +610,14 @@ fn load_byte_array_pool( while cursor.position() < u64::from(table.count) { let size = read_uleb_u32_internal(&mut cursor)? as usize; if size > std::u16::MAX as usize { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad ByteArray pool size".to_string())); } let mut byte_array: Vec = vec![0u8; size]; if let Ok(count) = cursor.read(&mut byte_array) { if count != size { - return Err(VMStatus::new(StatusCode::MALFORMED)); + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Bad ByteArray pool size".to_string())); } byte_arrays.push(byte_array); @@ -563,100 +626,30 @@ fn load_byte_array_pool( Ok(()) } -/// Builds the `TypeSignaturePool`. -fn load_type_signatures( +/// Builds the `SignaturePool`. +fn load_signatures( binary: &[u8], table: &Table, - type_signatures: &mut TypeSignaturePool, + signatures: &mut SignaturePool, ) -> BinaryLoaderResult<()> { let start = table.offset as usize; let end = start + table.count as usize; let mut cursor = Cursor::new(&binary[start..end]); while cursor.position() < u64::from(table.count) { - if let Ok(byte) = cursor.read_u8() { - if byte != SignatureType::TYPE_SIGNATURE as u8 { - return Err(VMStatus::new(StatusCode::UNEXPECTED_SIGNATURE_TYPE)); - } - } - let token = load_signature_token(&mut cursor)?; - type_signatures.push(TypeSignature(token)); + signatures.push(Signature(load_signature_tokens(&mut cursor)?)); } Ok(()) } -/// Builds the `FunctionSignaturePool`. -fn load_function_signatures( - binary: &[u8], - table: &Table, - function_signatures: &mut FunctionSignaturePool, -) -> BinaryLoaderResult<()> { - let start = table.offset as usize; - let end = start + table.count as usize; - let mut cursor = Cursor::new(&binary[start..end]); - while cursor.position() < u64::from(table.count) { - if let Ok(byte) = cursor.read_u8() { - if byte != SignatureType::FUNCTION_SIGNATURE as u8 { - return Err(VMStatus::new(StatusCode::UNEXPECTED_SIGNATURE_TYPE)); - } - } - - // Return signature - let token_count = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; - let mut returns_signature: Vec = Vec::new(); - for _i in 0..token_count { - let token = load_signature_token(&mut cursor)?; - returns_signature.push(token); - } - - // Arguments signature - let token_count = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; - let mut args_signature: Vec = Vec::new(); - for _i in 0..token_count { - let token = load_signature_token(&mut cursor)?; - args_signature.push(token); - } - let type_formals = load_kinds(&mut cursor)?; - function_signatures.push(FunctionSignature { - return_types: returns_signature, - arg_types: args_signature, - type_formals, - }); - } - Ok(()) -} - -/// Builds the `LocalsSignaturePool`. -fn load_locals_signatures( - binary: &[u8], - table: &Table, - locals_signatures: &mut LocalsSignaturePool, -) -> BinaryLoaderResult<()> { - let start = table.offset as usize; - let end = start + table.count as usize; - let mut cursor = Cursor::new(&binary[start..end]); - while cursor.position() < u64::from(table.count) { - if let Ok(byte) = cursor.read_u8() { - if byte != SignatureType::LOCAL_SIGNATURE as u8 { - return Err(VMStatus::new(StatusCode::UNEXPECTED_SIGNATURE_TYPE)); - } - } - - let token_count = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; - let mut local_signature: Vec = Vec::new(); - for _i in 0..token_count { - let token = load_signature_token(&mut cursor)?; - local_signature.push(token); - } - - locals_signatures.push(LocalsSignature(local_signature)); +fn load_signature_tokens(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult> { + let len = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; + let mut tokens = vec![]; + for _ in 0..len { + tokens.push(load_signature_token(cursor)?); } - Ok(()) + Ok(tokens) } /// Deserializes a `SignatureToken`. @@ -682,8 +675,15 @@ fn load_signature_token(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { let sh_idx = read_uleb_u16_internal(cursor)?; - let types = load_signature_tokens(cursor)?; - Ok(SignatureToken::Struct(StructHandleIndex(sh_idx), types)) + Ok(SignatureToken::Struct(StructHandleIndex(sh_idx))) + } + SerializedType::STRUCT_INST => { + let sh_idx = read_uleb_u16_internal(cursor)?; + let type_params = load_signature_tokens(cursor)?; + Ok(SignatureToken::StructInstantiation( + StructHandleIndex(sh_idx), + type_params, + )) } SerializedType::TYPE_PARAMETER => { let idx = read_uleb_u16_internal(cursor)?; @@ -691,19 +691,10 @@ fn load_signature_token(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult) -> BinaryLoaderResult> { - let len = read_uleb_u16_internal(cursor)?; - let mut tokens = vec![]; - for _ in 0..len { - tokens.push(load_signature_token(cursor)?); - } - Ok(tokens) -} - fn load_nominal_resource_flag(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { if let Ok(byte) = cursor.read_u8() { Ok(match SerializedNominalResourceFlag::from_u8(byte)? { @@ -711,7 +702,7 @@ fn load_nominal_resource_flag(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult< SerializedNominalResourceFlag::NORMAL_STRUCT => false, }) } else { - Err(VMStatus::new(StatusCode::MALFORMED)) + Err(VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string())) } } @@ -719,11 +710,11 @@ fn load_kind(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { if let Ok(byte) = cursor.read_u8() { Ok(match SerializedKind::from_u8(byte)? { SerializedKind::ALL => Kind::All, - SerializedKind::UNRESTRICTED => Kind::Unrestricted, + SerializedKind::COPYABLE => Kind::Copyable, SerializedKind::RESOURCE => Kind::Resource, }) } else { - Err(VMStatus::new(StatusCode::MALFORMED)) + Err(VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string())) } } @@ -749,28 +740,16 @@ fn load_struct_defs( let struct_handle = read_uleb_u16_internal(&mut cursor)?; let field_information_flag = match cursor.read_u8() { Ok(byte) => SerializedNativeStructFlag::from_u8(byte)?, - Err(_) => return Err(VMStatus::new(StatusCode::MALFORMED)), + Err(_) => { + return Err(VMStatus::new(StatusCode::MALFORMED) + .with_message("Invalid field info in struct".to_string())) + } }; let field_information = match field_information_flag { - SerializedNativeStructFlag::NATIVE => { - let field_count = read_uleb_u16_internal(&mut cursor)?; - if field_count != 0 { - return Err(VMStatus::new(StatusCode::MALFORMED)); - } - let fields_u16 = read_uleb_u16_internal(&mut cursor)?; - if fields_u16 != 0 { - return Err(VMStatus::new(StatusCode::MALFORMED)); - } - StructFieldInformation::Native - } + SerializedNativeStructFlag::NATIVE => StructFieldInformation::Native, SerializedNativeStructFlag::DECLARED => { - let field_count = read_uleb_u16_internal(&mut cursor)?; - let fields_u16 = read_uleb_u16_internal(&mut cursor)?; - let fields = FieldDefinitionIndex(fields_u16); - StructFieldInformation::Declared { - field_count, - fields, - } + let fields = load_field_defs(&mut cursor)?; + StructFieldInformation::Declared(fields) } }; struct_defs.push(StructDefinition { @@ -781,40 +760,80 @@ fn load_struct_defs( Ok(()) } -/// Builds the `FieldDefinition` table. -fn load_field_defs( +fn load_field_defs(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult> { + let mut fields = Vec::new(); + let field_count = read_uleb_u32_internal(cursor)?; + for _ in 0..field_count { + fields.push(load_field_def(cursor)?); + } + Ok(fields) +} + +fn load_field_def(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { + let name = read_uleb_u16_internal(cursor)?; + let signature = load_signature_token(cursor)?; + Ok(FieldDefinition { + name: IdentifierIndex(name), + signature: TypeSignature(signature), + }) +} + +/// Builds the `FunctionDefinition` table. +fn load_function_defs( binary: &[u8], table: &Table, - field_defs: &mut Vec, + func_defs: &mut Vec, ) -> BinaryLoaderResult<()> { let start = table.offset as usize; let end = start + table.count as usize; let mut cursor = Cursor::new(&binary[start..end]); while cursor.position() < u64::from(table.count) { - let struct_ = read_uleb_u16_internal(&mut cursor)?; - let name = read_uleb_u16_internal(&mut cursor)?; - let signature = read_uleb_u16_internal(&mut cursor)?; - field_defs.push(FieldDefinition { - struct_: StructHandleIndex(struct_), - name: IdentifierIndex(name), - signature: TypeSignatureIndex(signature), + let func_def = load_function_def(&mut cursor)?; + func_defs.push(func_def); + } + Ok(()) +} + +fn load_field_handles( + binary: &[u8], + table: &Table, + field_handles: &mut Vec, +) -> BinaryLoaderResult<()> { + let start = table.offset as usize; + let end = start + table.count as usize; + let mut cursor = Cursor::new(&binary[start..end]); + loop { + if cursor.position() == u64::from(table.count) { + break; + } + let struct_idx = read_uleb_u16_internal(&mut cursor)?; + let offset = read_uleb_u16_internal(&mut cursor)?; + field_handles.push(FieldHandle { + owner: StructDefinitionIndex(struct_idx), + field: offset, }); } Ok(()) } -/// Builds the `FunctionDefinition` table. -fn load_function_defs( +fn load_field_instantiations( binary: &[u8], table: &Table, - func_defs: &mut Vec, + field_insts: &mut Vec, ) -> BinaryLoaderResult<()> { let start = table.offset as usize; let end = start + table.count as usize; let mut cursor = Cursor::new(&binary[start..end]); - while cursor.position() < u64::from(table.count) { - let func_def = load_function_def(&mut cursor)?; - func_defs.push(func_def); + loop { + if cursor.position() == u64::from(table.count) { + break; + } + let handle = read_uleb_u16_internal(&mut cursor)?; + let inst = read_uleb_u16_internal(&mut cursor)?; + field_insts.push(FieldInstantiation { + handle: FieldHandleIndex(handle), + type_parameters: SignatureIndex(inst), + }); } Ok(()) } @@ -823,9 +842,9 @@ fn load_function_defs( fn load_function_def(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { let function = read_uleb_u16_internal(cursor)?; - let flags = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let flags = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; let acquires_global_resources = load_struct_definition_indices(cursor)?; let code_unit = load_code_unit(cursor)?; Ok(FunctionDefinition { @@ -840,9 +859,9 @@ fn load_function_def(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult, ) -> BinaryLoaderResult> { - let len = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let len = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; let mut indices = vec![]; for _ in 0..len { indices.push(StructDefinitionIndex(read_uleb_u16_internal(cursor)?)); @@ -857,7 +876,7 @@ fn load_code_unit(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { let mut code_unit = CodeUnit { max_stack_size, - locals: LocalsSignatureIndex(locals), + locals: SignatureIndex(locals), code: vec![], }; @@ -869,9 +888,9 @@ fn load_code_unit(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoaderResult<()> { let bytecode_count = read_u16_internal(cursor)?; while code.len() < bytecode_count as usize { - let byte = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let byte = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; let bytecode = match Opcodes::from_u8(byte)? { Opcodes::POP => Bytecode::Pop, Opcodes::RET => Bytecode::Ret, @@ -888,9 +907,9 @@ fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoad Bytecode::Branch(jump) } Opcodes::LD_U8 => { - let value = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let value = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::LdU8(value) } Opcodes::LD_U64 => { @@ -911,42 +930,50 @@ fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoad Opcodes::LD_TRUE => Bytecode::LdTrue, Opcodes::LD_FALSE => Bytecode::LdFalse, Opcodes::COPY_LOC => { - let idx = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let idx = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::CopyLoc(idx) } Opcodes::MOVE_LOC => { - let idx = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let idx = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::MoveLoc(idx) } Opcodes::ST_LOC => { - let idx = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let idx = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::StLoc(idx) } Opcodes::MUT_BORROW_LOC => { - let idx = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let idx = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::MutBorrowLoc(idx) } Opcodes::IMM_BORROW_LOC => { - let idx = cursor - .read_u8() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED))?; + let idx = cursor.read_u8().map_err(|_| { + VMStatus::new(StatusCode::MALFORMED).with_message("Unexpected EOF".to_string()) + })?; Bytecode::ImmBorrowLoc(idx) } Opcodes::MUT_BORROW_FIELD => { let idx = read_uleb_u16_internal(cursor)?; - Bytecode::MutBorrowField(FieldDefinitionIndex(idx)) + Bytecode::MutBorrowField(FieldHandleIndex(idx)) + } + Opcodes::MUT_BORROW_FIELD_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::MutBorrowFieldGeneric(FieldInstantiationIndex(idx)) } Opcodes::IMM_BORROW_FIELD => { let idx = read_uleb_u16_internal(cursor)?; - Bytecode::ImmBorrowField(FieldDefinitionIndex(idx)) + Bytecode::ImmBorrowField(FieldHandleIndex(idx)) + } + Opcodes::IMM_BORROW_FIELD_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::ImmBorrowFieldGeneric(FieldInstantiationIndex(idx)) } Opcodes::LD_BYTEARRAY => { let idx = read_uleb_u16_internal(cursor)?; @@ -954,18 +981,27 @@ fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoad } Opcodes::CALL => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::Call(FunctionHandleIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::Call(FunctionHandleIndex(idx)) + } + Opcodes::CALL_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::CallGeneric(FunctionInstantiationIndex(idx)) } Opcodes::PACK => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::Pack(StructDefinitionIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::Pack(StructDefinitionIndex(idx)) + } + Opcodes::PACK_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::PackGeneric(StructDefInstantiationIndex(idx)) } Opcodes::UNPACK => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::Unpack(StructDefinitionIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::Unpack(StructDefinitionIndex(idx)) + } + Opcodes::UNPACK_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::UnpackGeneric(StructDefInstantiationIndex(idx)) } Opcodes::READ_REF => Bytecode::ReadRef, Opcodes::WRITE_REF => Bytecode::WriteRef, @@ -995,34 +1031,43 @@ fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoad Opcodes::GET_TXN_SENDER => Bytecode::GetTxnSenderAddress, Opcodes::EXISTS => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::Exists(StructDefinitionIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::Exists(StructDefinitionIndex(idx)) + } + Opcodes::EXISTS_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::ExistsGeneric(StructDefInstantiationIndex(idx)) } Opcodes::MUT_BORROW_GLOBAL => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::MutBorrowGlobal( - StructDefinitionIndex(idx), - LocalsSignatureIndex(types_idx), - ) + Bytecode::MutBorrowGlobal(StructDefinitionIndex(idx)) + } + Opcodes::MUT_BORROW_GLOBAL_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::MutBorrowGlobalGeneric(StructDefInstantiationIndex(idx)) } Opcodes::IMM_BORROW_GLOBAL => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::ImmBorrowGlobal( - StructDefinitionIndex(idx), - LocalsSignatureIndex(types_idx), - ) + Bytecode::ImmBorrowGlobal(StructDefinitionIndex(idx)) + } + Opcodes::IMM_BORROW_GLOBAL_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::ImmBorrowGlobalGeneric(StructDefInstantiationIndex(idx)) } Opcodes::MOVE_FROM => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::MoveFrom(StructDefinitionIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::MoveFrom(StructDefinitionIndex(idx)) + } + Opcodes::MOVE_FROM_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::MoveFromGeneric(StructDefInstantiationIndex(idx)) } Opcodes::MOVE_TO => { let idx = read_uleb_u16_internal(cursor)?; - let types_idx = read_uleb_u16_internal(cursor)?; - Bytecode::MoveToSender(StructDefinitionIndex(idx), LocalsSignatureIndex(types_idx)) + Bytecode::MoveToSender(StructDefinitionIndex(idx)) + } + Opcodes::MOVE_TO_GENERIC => { + let idx = read_uleb_u16_internal(cursor)?; + Bytecode::MoveToSenderGeneric(StructDefInstantiationIndex(idx)) } Opcodes::GET_TXN_SEQUENCE_NUMBER => Bytecode::GetTxnSequenceNumber, Opcodes::GET_TXN_PUBLIC_KEY => Bytecode::GetTxnPublicKey, @@ -1038,35 +1083,35 @@ fn load_code(cursor: &mut Cursor<&[u8]>, code: &mut Vec) -> BinaryLoad // fn read_uleb_u16_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { - read_uleb128_as_u16(cursor).map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + read_uleb128_as_u16(cursor).map_err(|_| VMStatus::new(StatusCode::BAD_ULEB_U16)) } fn read_uleb_u32_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { - read_uleb128_as_u32(cursor).map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + read_uleb128_as_u32(cursor).map_err(|_| VMStatus::new(StatusCode::BAD_ULEB_U32)) } fn read_u16_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { cursor .read_u16::() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + .map_err(|_| VMStatus::new(StatusCode::BAD_U16)) } fn read_u32_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { cursor .read_u32::() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + .map_err(|_| VMStatus::new(StatusCode::BAD_U32)) } fn read_u64_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { cursor .read_u64::() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + .map_err(|_| VMStatus::new(StatusCode::BAD_U64)) } fn read_u128_internal(cursor: &mut Cursor<&[u8]>) -> BinaryLoaderResult { cursor .read_u128::() - .map_err(|_| VMStatus::new(StatusCode::MALFORMED)) + .map_err(|_| VMStatus::new(StatusCode::BAD_U128)) } impl TableType { @@ -1075,33 +1120,22 @@ impl TableType { 0x1 => Ok(TableType::MODULE_HANDLES), 0x2 => Ok(TableType::STRUCT_HANDLES), 0x3 => Ok(TableType::FUNCTION_HANDLES), - 0x4 => Ok(TableType::ADDRESS_POOL), - 0x5 => Ok(TableType::IDENTIFIERS), - 0x6 => Ok(TableType::BYTE_ARRAY_POOL), - 0x7 => Ok(TableType::MAIN), - 0x8 => Ok(TableType::STRUCT_DEFS), - 0x9 => Ok(TableType::FIELD_DEFS), - 0xA => Ok(TableType::FUNCTION_DEFS), - 0xB => Ok(TableType::TYPE_SIGNATURES), - 0xC => Ok(TableType::FUNCTION_SIGNATURES), - 0xD => Ok(TableType::LOCALS_SIGNATURES), + 0x4 => Ok(TableType::FUNCTION_INST), + 0x5 => Ok(TableType::SIGNATURES), + 0x6 => Ok(TableType::ADDRESS_POOL), + 0x7 => Ok(TableType::IDENTIFIERS), + 0x8 => Ok(TableType::BYTE_ARRAY_POOL), + 0x9 => Ok(TableType::MAIN), + 0xA => Ok(TableType::STRUCT_DEFS), + 0xB => Ok(TableType::STRUCT_DEF_INST), + 0xC => Ok(TableType::FUNCTION_DEFS), + 0xD => Ok(TableType::FIELD_HANDLE), + 0xE => Ok(TableType::FIELD_INST), _ => Err(VMStatus::new(StatusCode::UNKNOWN_TABLE_TYPE)), } } } -#[allow(dead_code)] -impl SignatureType { - fn from_u8(value: u8) -> BinaryLoaderResult { - match value { - 0x1 => Ok(SignatureType::TYPE_SIGNATURE), - 0x2 => Ok(SignatureType::FUNCTION_SIGNATURE), - 0x3 => Ok(SignatureType::LOCAL_SIGNATURE), - _ => Err(VMStatus::new(StatusCode::UNKNOWN_SIGNATURE_TYPE)), - } - } -} - impl SerializedType { fn from_u8(value: u8) -> BinaryLoaderResult { match value { @@ -1115,6 +1149,7 @@ impl SerializedType { 0x8 => Ok(SerializedType::STRUCT), 0x9 => Ok(SerializedType::TYPE_PARAMETER), 0xA => Ok(SerializedType::VECTOR), + 0xB => Ok(SerializedType::STRUCT_INST), _ => Err(VMStatus::new(StatusCode::UNKNOWN_SERIALIZED_TYPE)), } } @@ -1125,7 +1160,7 @@ impl SerializedNominalResourceFlag { match value { 0x1 => Ok(SerializedNominalResourceFlag::NOMINAL_RESOURCE), 0x2 => Ok(SerializedNominalResourceFlag::NORMAL_STRUCT), - _ => Err(VMStatus::new(StatusCode::UNKNOWN_SERIALIZED_TYPE)), + _ => Err(VMStatus::new(StatusCode::UNKNOWN_NOMINAL_RESOURCE)), } } } @@ -1134,9 +1169,9 @@ impl SerializedKind { fn from_u8(value: u8) -> BinaryLoaderResult { match value { 0x1 => Ok(SerializedKind::ALL), - 0x2 => Ok(SerializedKind::UNRESTRICTED), + 0x2 => Ok(SerializedKind::COPYABLE), 0x3 => Ok(SerializedKind::RESOURCE), - _ => Err(VMStatus::new(StatusCode::UNKNOWN_SERIALIZED_TYPE)), + _ => Err(VMStatus::new(StatusCode::UNKNOWN_KIND)), } } } @@ -1146,7 +1181,7 @@ impl SerializedNativeStructFlag { match value { 0x1 => Ok(SerializedNativeStructFlag::NATIVE), 0x2 => Ok(SerializedNativeStructFlag::DECLARED), - _ => Err(VMStatus::new(StatusCode::UNKNOWN_SERIALIZED_TYPE)), + _ => Err(VMStatus::new(StatusCode::UNKNOWN_NATIVE_STRUCT_FLAG)), } } } @@ -1213,6 +1248,16 @@ impl Opcodes { 0x39 => Ok(Opcodes::CAST_U8), 0x3A => Ok(Opcodes::CAST_U64), 0x3B => Ok(Opcodes::CAST_U128), + 0x3C => Ok(Opcodes::MUT_BORROW_FIELD_GENERIC), + 0x3D => Ok(Opcodes::IMM_BORROW_FIELD_GENERIC), + 0x3E => Ok(Opcodes::CALL_GENERIC), + 0x3F => Ok(Opcodes::PACK_GENERIC), + 0x40 => Ok(Opcodes::UNPACK_GENERIC), + 0x41 => Ok(Opcodes::EXISTS_GENERIC), + 0x42 => Ok(Opcodes::MUT_BORROW_GLOBAL_GENERIC), + 0x43 => Ok(Opcodes::IMM_BORROW_GLOBAL_GENERIC), + 0x44 => Ok(Opcodes::MOVE_FROM_GENERIC), + 0x45 => Ok(Opcodes::MOVE_TO_GENERIC), _ => Err(VMStatus::new(StatusCode::UNKNOWN_OPCODE)), } } diff --git a/language/vm/src/file_format.rs b/language/vm/src/file_format.rs index 6a5496c49aca..b73bed9349f9 100644 --- a/language/vm/src/file_format.rs +++ b/language/vm/src/file_format.rs @@ -104,6 +104,26 @@ define_index! { kind: FunctionHandle, doc: "Index into the `FunctionHandle` table.", } +define_index! { + name: FieldHandleIndex, + kind: FieldHandle, + doc: "Index into the `FieldHandle` table.", +} +define_index! { + name: StructDefInstantiationIndex, + kind: StructDefInstantiation, + doc: "Index into the `StructInstantiation` table.", +} +define_index! { + name: FunctionInstantiationIndex, + kind: FunctionInstantiation, + doc: "Index into the `FunctionInstantiation` table.", +} +define_index! { + name: FieldInstantiationIndex, + kind: FieldInstantiation, + doc: "Index into the `FieldInstantiation` table.", +} define_index! { name: IdentifierIndex, kind: Identifier, @@ -120,30 +140,15 @@ define_index! { doc: "Index into the `AddressPool` table.", } define_index! { - name: TypeSignatureIndex, - kind: TypeSignature, - doc: "Index into the `TypeSignature` table.", -} -define_index! { - name: FunctionSignatureIndex, - kind: FunctionSignature, - doc: "Index into the `FunctionSignature` table.", -} -define_index! { - name: LocalsSignatureIndex, - kind: LocalsSignature, - doc: "Index into the `LocalsSignature` table.", + name: SignatureIndex, + kind: Signature, + doc: "Index into the `Signature` table.", } define_index! { name: StructDefinitionIndex, kind: StructDefinition, doc: "Index into the `StructDefinition` table.", } -define_index! { - name: FieldDefinitionIndex, - kind: FieldDefinition, - doc: "Index into the `FieldDefinition` table.", -} define_index! { name: FunctionDefinitionIndex, kind: FunctionDefinition, @@ -172,11 +177,9 @@ pub type AddressPool = Vec; /// The pool of `TypeSignature` instances. Those are system and user types used and /// their composition (e.g. &U64). pub type TypeSignaturePool = Vec; -/// The pool of `FunctionSignature` instances. -pub type FunctionSignaturePool = Vec; -/// The pool of `LocalsSignature` instances. Every function definition must define the set of +/// The pool of `Signature` instances. Every function definition must define the set of /// locals used and their types. -pub type LocalsSignaturePool = Vec; +pub type SignaturePool = Vec; // TODO: "" only passes the validator for identifiers because it is special cased. Whenever // "" is removed, so should the special case in identifier.rs. @@ -185,9 +188,9 @@ pub fn self_module_name() -> &'static IdentStr { } /// Index 0 into the LocalsSignaturePool, which is guaranteed to be an empty list. -/// Used to represent function/struct instantiation with no type actuals -- effectively +/// Used to represent function/struct instantiation with no type arguments -- effectively /// non-generic functions and structs. -pub const NO_TYPE_ACTUALS: LocalsSignatureIndex = LocalsSignatureIndex(0); +pub const NO_TYPE_ARGUMENTS: SignatureIndex = SignatureIndex(0); // HANDLES: // Handles are structs that accompany opcodes that need references: a type reference, @@ -247,7 +250,7 @@ pub struct StructHandle { /// If `is_nominal_resource` is true, it is a *nominal resource* pub is_nominal_resource: bool, /// The type formals (identified by their index into the vec) and their kind constraints - pub type_formals: Vec, + pub type_parameters: Vec, } /// A `FunctionHandle` is a reference to a function. It is composed by a @@ -259,14 +262,27 @@ pub struct StructHandle { /// function calls. #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] -#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))] pub struct FunctionHandle { /// The module that defines the function. pub module: ModuleHandleIndex, /// The name of the function. pub name: IdentifierIndex, - /// The signature of the function. - pub signature: FunctionSignatureIndex, + /// The list of arguments to the function. + pub parameters: SignatureIndex, + /// The list of return types. + pub return_: SignatureIndex, + /// The type formals (identified by their index into the vec) and their kind constraints + pub type_parameters: Vec, +} + +/// A field access info (owner type and offset) +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] +pub struct FieldHandle { + pub owner: StructDefinitionIndex, + pub field: MemberCount, } // DEFINITIONS: @@ -278,13 +294,47 @@ pub struct FunctionHandle { #[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] pub enum StructFieldInformation { Native, - Declared { - /// The number of fields in this type. - field_count: MemberCount, - /// The starting index for the fields of this type. `FieldDefinition`s for each type must - /// be consecutively stored in the `FieldDefinition` table. - fields: FieldDefinitionIndex, - }, + Declared(Vec), +} + +// +// Instantiations +// +// Instantiations point to a generic handle and its instantiation. +// The instantiation can be partial. +// So, for example, `S`, `S`, `S`, `S, address>` are all +// `StructInstantiation`s + +/// A complete or partial instantiation of a generic struct +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] +pub struct StructDefInstantiation { + pub def: StructDefinitionIndex, + pub type_parameters: SignatureIndex, +} + +/// A complete or partial instantiation of a function +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] +pub struct FunctionInstantiation { + pub handle: FunctionHandleIndex, + pub type_parameters: SignatureIndex, +} + +/// A complete or partial instantiation of a field (or the type of it). +/// +/// A `FieldInstantiation` points to a generic `FieldHandle` and the instantiation +/// of the owner type. +/// E.g. for `S.f` where `f` is a field of any type, `instantiation` +/// would be `[u8, boo]` +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] +pub struct FieldInstantiation { + pub handle: FieldHandleIndex, + pub type_parameters: SignatureIndex, } /// A `StructDefinition` is a type definition. It either indicates it is native or @@ -298,7 +348,7 @@ pub struct StructDefinition { pub struct_handle: StructHandleIndex, /// Contains either /// - Information indicating the struct is native and has no accessible fields - /// - Information indicating the number of fields and the start `FieldDefinitionIndex` + /// - Information indicating the number of fields and the start `FieldDefinition`s pub field_information: StructFieldInformation, } @@ -307,22 +357,19 @@ impl StructDefinition { match &self.field_information { // TODO we might want a more informative error here StructFieldInformation::Native => Err(VMStatus::new(StatusCode::LINKER_ERROR)), - StructFieldInformation::Declared { field_count, .. } => Ok(*field_count), + StructFieldInformation::Declared(fields) => Ok(fields.len() as u16), } } } -/// A `FieldDefinition` is the definition of a field: the type the field is defined on, -/// its name and the field type. +/// A `FieldDefinition` is the definition of a field: its name and the field type. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] #[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] pub struct FieldDefinition { - /// The type (resource or unrestricted) the field is defined on. - pub struct_: StructHandleIndex, /// The name of the field. pub name: IdentifierIndex, /// The type of the field. - pub signature: TypeSignatureIndex, + pub signature: TypeSignature, } /// A `FunctionDefinition` is the implementation of a function. It defines @@ -342,7 +389,7 @@ pub struct FunctionDefinition { /// /// Not in the signature as it is not needed outside of the declaring module /// - /// Note, there is no LocalsSignatureIndex with each struct definition index, as global + /// Note, there is no SignatureIndex with each struct definition index, as global /// resources cannot currently take type arguments pub acquires_global_resources: Vec, /// Code for this function. @@ -376,11 +423,9 @@ impl FunctionDefinition { #[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))] pub struct TypeSignature(pub SignatureToken); -/// A `FunctionSignature` describes the types of a function. -/// -/// The `FunctionSignature` is polymorphic: it can have type parameters in the argument and return -/// types and carries kind constraints for those type parameters (empty list for non-generic -/// functions). +// TODO: remove at some point or move it in the front end (language/compiler) +/// A `FunctionSignature` in internally used to create a unique representation of the overall +/// signature as need. Consider deprecated... #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] #[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))] @@ -390,25 +435,25 @@ pub struct FunctionSignature { any(test, feature = "fuzzing"), proptest(strategy = "vec(any::(), 0..=params)") )] - pub return_types: Vec, + pub return_: Vec, /// The list of arguments to the function. #[cfg_attr( any(test, feature = "fuzzing"), proptest(strategy = "vec(any::(), 0..=params)") )] - pub arg_types: Vec, + pub parameters: Vec, /// The type formals (identified by their index into the vec) and their kind constraints - pub type_formals: Vec, + pub type_parameters: Vec, } -/// A `LocalsSignature` is the list of locals used by a function. +/// A `Signature` is the list of locals used by a function. /// /// Locals include the arguments to the function from position `0` to argument `count - 1`. /// The remaining elements are the type of each local. #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] #[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] #[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))] -pub struct LocalsSignature( +pub struct Signature( #[cfg_attr( any(test, feature = "fuzzing"), proptest(strategy = "vec(any::(), 0..=params)") @@ -416,8 +461,8 @@ pub struct LocalsSignature( pub Vec, ); -impl LocalsSignature { - /// Length of the `LocalsSignature`. +impl Signature { + /// Length of the `Signature`. #[inline] pub fn len(&self) -> usize { self.0.len() @@ -431,28 +476,28 @@ impl LocalsSignature { } /// Type parameters are encoded as indices. This index can also be used to lookup the kind of a -/// type parameter in the `FunctionSignature/Handle` and `StructHandle`. +/// type parameter in the `FunctionHandle` and `StructHandle`. pub type TypeParameterIndex = u16; /// A `Kind` classifies types into sets with rules each set must follow. /// -/// Currently there are three kinds in Move: `All`, `Resource` and `Unrestricted`. +/// Currently there are three kinds in Move: `All`, `Resource` and `Copyable`. #[derive(Debug, Clone, Eq, Copy, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] pub enum Kind { /// Represents the super set of all types. The type might actually be a `Resource` or - /// `Unrestricted` A type might be in this set if it is not known to be a `Resource` or - /// `Unrestricted` + /// `Copyable` A type might be in this set if it is not known to be a `Resource` or + /// `Copyable` /// - This occurs when there is a type parameter with this kind as a constraint All, /// `Resource` types must follow move semantics and various resource safety rules, namely: /// - `Resource` values cannot be copied /// - `Resource` values cannot be popped, i.e. they must be used Resource, - /// `Unrestricted` types do not need to follow the `Resource` rules. - /// - `Unrestricted` values can be copied - /// - `Unrestricted` values can be popped - Unrestricted, + /// `Copyable` types do not need to follow the `Resource` rules. + /// - `Copyable` values can be copied + /// - `Copyable` values can be popped + Copyable, } impl Kind { @@ -461,16 +506,16 @@ impl Kind { pub fn is_sub_kind_of(self, k: Kind) -> bool { use Kind::*; - matches!((self, k), (_, All) | (Resource, Resource) | (Unrestricted, Unrestricted)) + matches!((self, k), (_, All) | (Resource, Resource) | (Copyable, Copyable)) } /// Helper function to determine the kind of a struct instance by taking the kind of a type - /// actual and join it with the existing partial result. + /// argument and join it with the existing partial result. pub fn join(self, other: Kind) -> Kind { match (self, other) { (Kind::All, _) | (_, Kind::All) => Kind::All, (Kind::Resource, _) | (_, Kind::Resource) => Kind::Resource, - (Kind::Unrestricted, Kind::Unrestricted) => Kind::Unrestricted, + (Kind::Copyable, Kind::Copyable) => Kind::Copyable, } } } @@ -496,8 +541,9 @@ pub enum SignatureToken { Address, /// Vector Vector(Box), - /// MOVE user type, resource or unrestricted - Struct(StructHandleIndex, Vec), + /// MOVE user type, resource or copyable + Struct(StructHandleIndex), + StructInstantiation(StructHandleIndex, Vec), /// Reference to a type. Reference(Box), /// Mutable reference to a type. @@ -521,8 +567,7 @@ impl Arbitrary for SignatureToken { Just(U64), Just(U128), Just(Address), - // TODO: generate type actuals when generics is implemented - any::().prop_map(|sh_idx| Struct(sh_idx, vec![])), + any::().prop_map(Struct), any::().prop_map(TypeParameter), ]; leaf.prop_recursive( @@ -550,7 +595,10 @@ impl std::fmt::Debug for SignatureToken { SignatureToken::U128 => write!(f, "U128"), SignatureToken::Address => write!(f, "Address"), SignatureToken::Vector(boxed) => write!(f, "Vector({:?})", boxed), - SignatureToken::Struct(idx, types) => write!(f, "Struct({:?}, {:?})", idx, types), + SignatureToken::Struct(idx) => write!(f, "Struct({:?})", idx), + SignatureToken::StructInstantiation(idx, types) => { + write!(f, "StructInstantiation({:?}, {:?})", idx, types) + } SignatureToken::Reference(boxed) => write!(f, "Reference({:?})", boxed), SignatureToken::MutableReference(boxed) => write!(f, "MutableReference({:?})", boxed), SignatureToken::TypeParameter(idx) => write!(f, "TypeParameter({:?})", idx), @@ -559,38 +607,6 @@ impl std::fmt::Debug for SignatureToken { } impl SignatureToken { - /// If a `SignatureToken` is a reference it returns the underlying type of the reference (e.g. - /// U64 for &U64). - #[inline] - pub fn get_struct_handle_from_reference( - reference_signature: &SignatureToken, - ) -> Option { - match reference_signature { - SignatureToken::Reference(signature) => match **signature { - SignatureToken::Struct(idx, _) => Some(idx), - _ => None, - }, - SignatureToken::MutableReference(signature) => match **signature { - SignatureToken::Struct(idx, _) => Some(idx), - _ => None, - }, - _ => None, - } - } - - /// Returns the type actuals if the signature token is a reference to a struct instance. - pub fn get_type_actuals_from_reference(&self) -> Option<&[SignatureToken]> { - use SignatureToken::*; - - match self { - Reference(box_) | MutableReference(box_) => match &**box_ { - Struct(_, tys) => Some(&tys), - _ => None, - }, - _ => None, - } - } - /// Returns the "value kind" for the `SignatureToken` #[inline] pub fn signature_token_kind(&self) -> SignatureTokenKind { @@ -601,36 +617,31 @@ impl SignatureToken { match self { Reference(_) => SignatureTokenKind::Reference, MutableReference(_) => SignatureTokenKind::MutableReference, - Bool | U8 | U64 | U128 | Address | Struct(_, _) | Vector(_) => { - SignatureTokenKind::Value - } + Bool + | U8 + | U64 + | U128 + | Address + | Struct(_) + | StructInstantiation(_, _) + | Vector(_) => SignatureTokenKind::Value, // TODO: This is a temporary hack to please the verifier. SignatureTokenKind will soon // be completely removed. `SignatureTokenView::kind()` should be used instead. TypeParameter(_) => SignatureTokenKind::Value, } } - /// Returns the `StructHandleIndex` for a `SignatureToken` that contains a reference to a user - /// defined type (a resource or unrestricted type). - #[inline] - pub fn struct_index(&self) -> Option { - use SignatureToken::*; - - match self { - Struct(sh_idx, _) => Some(*sh_idx), - Reference(token) | MutableReference(token) => token.struct_index(), - Bool | U8 | U64 | U128 | Address | Vector(_) | TypeParameter(_) => None, - } - } - /// Returns `true` if the `SignatureToken` is a primitive type. pub fn is_primitive(&self) -> bool { use SignatureToken::*; match self { Bool | U8 | U64 | U128 | Address => true, - Struct(_, _) | Reference(_) | Vector(_) | MutableReference(_) | TypeParameter(_) => { - false - } + Struct(_) + | StructInstantiation(_, _) + | Reference(_) + | Vector(_) + | MutableReference(_) + | TypeParameter(_) => false, } } @@ -642,7 +653,8 @@ impl SignatureToken { Bool | Address | Vector(_) - | Struct(_, _) + | Struct(_) + | StructInstantiation(_, _) | Reference(_) | MutableReference(_) | TypeParameter(_) => false, @@ -660,7 +672,7 @@ impl SignatureToken { pub fn allows_equality(&self) -> bool { use SignatureToken::*; match self { - Struct(_, _) => false, + Struct(_) | StructInstantiation(_, _) => false, Reference(token) | MutableReference(token) => token.is_primitive(), token => token.is_primitive(), } @@ -685,7 +697,7 @@ impl SignatureToken { /// Panics if this token doesn't contain a struct handle. pub fn debug_set_sh_idx(&mut self, sh_idx: StructHandleIndex) { match self { - SignatureToken::Struct(ref mut wrapped, _) => *wrapped = sh_idx, + SignatureToken::Struct(ref mut wrapped) => *wrapped = sh_idx, SignatureToken::Reference(ref mut token) | SignatureToken::MutableReference(ref mut token) => token.debug_set_sh_idx(sh_idx), other => panic!( @@ -694,81 +706,6 @@ impl SignatureToken { ), } } - - /// Creating a new type by Substituting the type variables with type actuals. - pub fn substitute(&self, tys: &[SignatureToken]) -> SignatureToken { - use SignatureToken::*; - - match self { - Bool => Bool, - U8 => U8, - U64 => U64, - U128 => U128, - Address => Address, - Vector(ty) => Vector(Box::new(ty.substitute(tys))), - Struct(idx, actuals) => Struct( - *idx, - actuals - .iter() - .map(|ty| ty.substitute(tys)) - .collect::>(), - ), - Reference(ty) => Reference(Box::new(ty.substitute(tys))), - MutableReference(ty) => MutableReference(Box::new(ty.substitute(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. - assume!((*idx as usize) < tys.len()); - tys[*idx as usize].clone() - } - } - } - - /// Returns the kind of the signature token in the given context (module, function/struct). - /// The context is needed to determine the kinds of structs & type variables. - pub fn kind( - (struct_handles, type_formals): (&[StructHandle], &[Kind]), - ty: &SignatureToken, - ) -> Kind { - use SignatureToken::*; - - match ty { - // The primitive types & references have kind unrestricted. - Bool | U8 | U64 | U128 | Address | Reference(_) | MutableReference(_) => { - Kind::Unrestricted - } - - // To get the kind of a type parameter, we lookup its constraint in the formals. - TypeParameter(idx) => type_formals[*idx as usize], - - Vector(ty) => Self::kind((struct_handles, type_formals), ty), - - Struct(idx, tys) => { - // Get the struct handle at idx. Note the index could be out of bounds. - let sh = &struct_handles[idx.0 as usize]; - - if sh.is_nominal_resource { - return Kind::Resource; - } - - // Gather the kinds of the type actuals. - let kinds = tys - .iter() - .map(|ty| Self::kind((struct_handles, type_formals), ty)) - .collect::>(); - - // Derive the kind of the struct. - // - If any of the type actuals is `all`, then the struct is `all`. - // - `all` means some part of the type can be either `resource` or - // `unrestricted`. - // - Therefore it is also impossible to determine the kind of the type as a - // whole, and thus `all`. - // - If none of the type actuals is `all`, then the struct is a resource if - // and only if one of the type actuals is `resource`. - kinds.iter().cloned().fold(Kind::Unrestricted, Kind::join) - } - } - } } /// A `CodeUnit` is the body of a function. It has the function header and the instruction stream. @@ -779,7 +716,7 @@ pub struct CodeUnit { /// Max stack size for the function - currently unused. pub max_stack_size: u16, /// List of locals type. All locals are typed. - pub locals: LocalsSignatureIndex, + pub locals: SignatureIndex, /// Code stream, function body. #[cfg_attr( any(test, feature = "fuzzing"), @@ -807,7 +744,7 @@ impl CodeUnit { #[num_variants = "NUM_INSTRUCTIONS"] pub enum Bytecode { /// Pop and discard the value at the top of the stack. - /// The value on the stack must be an unrestricted type. + /// The value on the stack must be an copyable type. /// /// Stack transition: /// @@ -933,7 +870,8 @@ pub enum Bytecode { /// /// ```..., arg(1), arg(2), ..., arg(n) -> ..., return_value(1), return_value(2), ..., /// return_value(k)``` - Call(FunctionHandleIndex, LocalsSignatureIndex), + Call(FunctionHandleIndex), + CallGeneric(FunctionInstantiationIndex), /// Create an instance of the type specified via `StructHandleIndex` and push it on the stack. /// The values of the fields of the struct, in the order they appear in the struct declaration, /// must be pushed on the stack. All fields must be provided. @@ -943,7 +881,8 @@ pub enum Bytecode { /// Stack transition: /// /// ```..., field(1)_value, field(2)_value, ..., field(n)_value -> ..., instance_value``` - Pack(StructDefinitionIndex, LocalsSignatureIndex), + Pack(StructDefinitionIndex), + PackGeneric(StructDefInstantiationIndex), /// Destroy an instance of a type and push the values bound to each field on the /// stack. /// @@ -956,7 +895,8 @@ pub enum Bytecode { /// Stack transition: /// /// ```..., instance_value -> ..., field(1)_value, field(2)_value, ..., field(n)_value``` - Unpack(StructDefinitionIndex, LocalsSignatureIndex), + Unpack(StructDefinitionIndex), + UnpackGeneric(StructDefInstantiationIndex), /// Read a reference. The reference is on the stack, it is consumed and the value read is /// pushed on the stack. /// @@ -970,7 +910,7 @@ pub enum Bytecode { /// Write to a reference. The reference and the value are on the stack and are consumed. /// /// - /// The reference must be to an unrestricted type because Resources cannot be overwritten. + /// The reference must be to an copyable type because Resources cannot be overwritten. /// /// Stack transition: /// @@ -998,21 +938,36 @@ pub enum Bytecode { /// /// ```... -> ..., reference``` ImmBorrowLoc(LocalIndex), - /// Load a mutable reference to a field identified by `FieldDefinitionIndex`. + /// Load a mutable reference to a field identified by `FieldHandleIndex`. /// The top of the stack must be a mutable reference to a type that contains the field /// definition. /// /// Stack transition: /// /// ```..., reference -> ..., field_reference``` - MutBorrowField(FieldDefinitionIndex), - /// Load an immutable reference to a field identified by `FieldDefinitionIndex`. + MutBorrowField(FieldHandleIndex), + /// Load a mutable reference to a field identified by `FieldInstantiationIndex`. + /// The top of the stack must be a mutable reference to a type that contains the field + /// definition. + /// + /// Stack transition: + /// + /// ```..., reference -> ..., field_reference``` + MutBorrowFieldGeneric(FieldInstantiationIndex), + /// Load an immutable reference to a field identified by `FieldHandleIndex`. + /// The top of the stack must be a reference to a type that contains the field definition. + /// + /// Stack transition: + /// + /// ```..., reference -> ..., field_reference``` + ImmBorrowField(FieldHandleIndex), + /// Load an immutable reference to a field identified by `FieldInstantiationIndex`. /// The top of the stack must be a reference to a type that contains the field definition. /// /// Stack transition: /// /// ```..., reference -> ..., field_reference``` - ImmBorrowField(FieldDefinitionIndex), + ImmBorrowFieldGeneric(FieldInstantiationIndex), /// Return a mutable reference to an instance of type `StructDefinitionIndex` published at the /// address passed as argument. Abort execution if such an object does not exist or if a /// reference has already been handed out. @@ -1020,7 +975,8 @@ pub enum Bytecode { /// Stack transition: /// /// ```..., address_value -> ..., reference_value``` - MutBorrowGlobal(StructDefinitionIndex, LocalsSignatureIndex), + MutBorrowGlobal(StructDefinitionIndex), + MutBorrowGlobalGeneric(StructDefInstantiationIndex), /// Return an immutable reference to an instance of type `StructDefinitionIndex` published at /// the address passed as argument. Abort execution if such an object does not exist or if a /// reference has already been handed out. @@ -1028,7 +984,8 @@ pub enum Bytecode { /// Stack transition: /// /// ```..., address_value -> ..., reference_value``` - ImmBorrowGlobal(StructDefinitionIndex, LocalsSignatureIndex), + ImmBorrowGlobal(StructDefinitionIndex), + ImmBorrowGlobalGeneric(StructDefInstantiationIndex), /// Add the 2 u64 at the top of the stack and pushes the result on the stack. /// The operation aborts the transaction in case of overflow. /// @@ -1182,21 +1139,24 @@ pub enum Bytecode { /// Stack transition: /// /// ```..., address_value -> ..., bool_value``` - Exists(StructDefinitionIndex, LocalsSignatureIndex), + Exists(StructDefinitionIndex), + ExistsGeneric(StructDefInstantiationIndex), /// Move the instance of type StructDefinitionIndex, at the address at the top of the stack. /// Abort execution if such an object does not exist. /// /// Stack transition: /// /// ```..., address_value -> ..., value``` - MoveFrom(StructDefinitionIndex, LocalsSignatureIndex), + MoveFrom(StructDefinitionIndex), + MoveFromGeneric(StructDefInstantiationIndex), /// Move the instance at the top of the stack to the address of the sender. /// Abort execution if an object of type StructDefinitionIndex already exists in address. /// /// Stack transition: /// /// ```..., value -> ...``` - MoveToSender(StructDefinitionIndex, LocalsSignatureIndex), + MoveToSender(StructDefinitionIndex), + MoveToSenderGeneric(StructDefInstantiationIndex), /// Get the sequence number submitted with the transaction and pushes it on the stack. /// /// Stack transition: @@ -1246,18 +1206,25 @@ impl ::std::fmt::Debug for Bytecode { Bytecode::CopyLoc(a) => write!(f, "CopyLoc({})", a), Bytecode::MoveLoc(a) => write!(f, "MoveLoc({})", a), Bytecode::StLoc(a) => write!(f, "StLoc({})", a), - Bytecode::Call(a, b) => write!(f, "Call({}, {:?})", a, b), - Bytecode::Pack(a, b) => write!(f, "Pack({}, {:?})", a, b), - Bytecode::Unpack(a, b) => write!(f, "Unpack({}, {:?})", a, b), + Bytecode::Call(a) => write!(f, "Call({})", a), + Bytecode::CallGeneric(a) => write!(f, "CallGeneric({})", a), + Bytecode::Pack(a) => write!(f, "Pack({})", a), + Bytecode::PackGeneric(a) => write!(f, "PackGeneric({})", a), + Bytecode::Unpack(a) => write!(f, "Unpack({})", a), + Bytecode::UnpackGeneric(a) => write!(f, "UnpackGeneric({})", a), Bytecode::ReadRef => write!(f, "ReadRef"), Bytecode::WriteRef => write!(f, "WriteRef"), Bytecode::FreezeRef => write!(f, "FreezeRef"), Bytecode::MutBorrowLoc(a) => write!(f, "MutBorrowLoc({})", a), Bytecode::ImmBorrowLoc(a) => write!(f, "ImmBorrowLoc({})", a), - Bytecode::MutBorrowField(a) => write!(f, "MutBorrowField({})", a), - Bytecode::ImmBorrowField(a) => write!(f, "ImmBorrowField({})", a), - Bytecode::MutBorrowGlobal(a, b) => write!(f, "MutBorrowGlobal({}, {:?})", a, b), - Bytecode::ImmBorrowGlobal(a, b) => write!(f, "ImmBorrowGlobal({}, {:?})", a, b), + Bytecode::MutBorrowField(a) => write!(f, "MutBorrowField({:?})", a), + Bytecode::MutBorrowFieldGeneric(a) => write!(f, "MutBorrowFieldGeneric({:?})", a), + Bytecode::ImmBorrowField(a) => write!(f, "ImmBorrowField({:?})", a), + Bytecode::ImmBorrowFieldGeneric(a) => write!(f, "ImmBorrowFieldGeneric({:?})", a), + Bytecode::MutBorrowGlobal(a) => write!(f, "MutBorrowGlobal({:?})", a), + Bytecode::MutBorrowGlobalGeneric(a) => write!(f, "MutBorrowGlobalGeneric({:?})", a), + Bytecode::ImmBorrowGlobal(a) => write!(f, "ImmBorrowGlobal({:?})", a), + Bytecode::ImmBorrowGlobalGeneric(a) => write!(f, "ImmBorrowGlobalGeneric({:?})", a), Bytecode::Add => write!(f, "Add"), Bytecode::Sub => write!(f, "Sub"), Bytecode::Mul => write!(f, "Mul"), @@ -1282,9 +1249,12 @@ impl ::std::fmt::Debug for Bytecode { Bytecode::GetTxnMaxGasUnits => write!(f, "GetTxnMaxGasUnits"), Bytecode::GetGasRemaining => write!(f, "GetGasRemaining"), Bytecode::GetTxnSenderAddress => write!(f, "GetTxnSenderAddress"), - Bytecode::Exists(a, b) => write!(f, "Exists({}, {:?})", a, b), - Bytecode::MoveFrom(a, b) => write!(f, "MoveFrom({}, {:?})", a, b), - Bytecode::MoveToSender(a, b) => write!(f, "MoveToSender({}, {:?})", a, b), + Bytecode::Exists(a) => write!(f, "Exists({:?})", a), + Bytecode::ExistsGeneric(a) => write!(f, "ExistsGeneric({:?})", a), + Bytecode::MoveFrom(a) => write!(f, "MoveFrom({:?})", a), + Bytecode::MoveFromGeneric(a) => write!(f, "MoveFromGeneric({:?})", a), + Bytecode::MoveToSender(a) => write!(f, "MoveToSender({:?})", a), + Bytecode::MoveToSenderGeneric(a) => write!(f, "MoveToSenderGeneric({:?})", a), Bytecode::GetTxnSequenceNumber => write!(f, "GetTxnSequenceNumber"), Bytecode::GetTxnPublicKey => write!(f, "GetTxnPublicKey"), } @@ -1374,12 +1344,10 @@ pub struct CompiledScriptMut { /// Handles to external/imported functions. pub function_handles: Vec, - /// Type pool. All external types referenced by the transaction. - pub type_signatures: TypeSignaturePool, - /// Function signature pool. The signatures of the function referenced by the transaction. - pub function_signatures: FunctionSignaturePool, - /// Locals signature pool. The signature of the locals in `main`. - pub locals_signatures: LocalsSignaturePool, + /// Function instantiations. + pub function_instantiations: Vec, + + pub signatures: SignaturePool, /// All identifiers used in this transaction. pub identifiers: IdentifierPool, @@ -1433,17 +1401,19 @@ impl CompiledScriptMut { module_handles: self.module_handles, struct_handles: self.struct_handles, function_handles: self.function_handles, + field_handles: vec![], + + struct_def_instantiations: vec![], + function_instantiations: self.function_instantiations, + field_instantiations: vec![], - type_signatures: self.type_signatures, - function_signatures: self.function_signatures, - locals_signatures: self.locals_signatures, + signatures: self.signatures, identifiers: self.identifiers, byte_array_pool: self.byte_array_pool, address_pool: self.address_pool, struct_defs: vec![], - field_defs: vec![], function_defs: vec![self.main], } } @@ -1468,15 +1438,19 @@ pub struct CompiledModuleMut { pub struct_handles: Vec, /// Handles to external and internal functions. pub function_handles: Vec, + /// Handles to fields. + pub field_handles: Vec, + + /// Struct instantiations. + pub struct_def_instantiations: Vec, + /// Function instantiations. + pub function_instantiations: Vec, + /// Field instantiations. + pub field_instantiations: Vec, - /// Type pool. A definition for all types used in the module. - pub type_signatures: TypeSignaturePool, - /// Function signature pool. Represents all function signatures defined or used in - /// the module. - pub function_signatures: FunctionSignaturePool, /// Locals signature pool. The signature for all locals of the functions defined in /// the module. - pub locals_signatures: LocalsSignaturePool, + pub signatures: SignaturePool, /// All identifiers used in this module. pub identifiers: IdentifierPool, @@ -1488,8 +1462,6 @@ pub struct CompiledModuleMut { /// Types defined in this module. pub struct_defs: Vec, - /// Fields defined on types in this module. - pub field_defs: Vec, /// Function defined in this module. pub function_defs: Vec, } @@ -1509,11 +1481,7 @@ impl Arbitrary for CompiledScriptMut { vec(any::(), 0..=size), vec(any::(), 0..=size), ), - ( - vec(any::(), 0..=size), - vec(any_with::(size), 0..=size), - vec(any_with::(size), 0..=size), - ), + vec(any_with::(size), 0..=size), ( vec(any::(), 0..=size), vec(vec(any::(), 0..=size), 0..=size), @@ -1524,7 +1492,7 @@ impl Arbitrary for CompiledScriptMut { .prop_map( |( (module_handles, struct_handles, function_handles), - (type_signatures, function_signatures, locals_signatures), + signatures, (identifiers, byte_array_pool, address_pool), main, )| { @@ -1532,9 +1500,8 @@ impl Arbitrary for CompiledScriptMut { module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + function_instantiations: vec![], + signatures, identifiers, byte_array_pool, address_pool, @@ -1559,11 +1526,7 @@ impl Arbitrary for CompiledModuleMut { vec(any::(), 0..=size), vec(any::(), 0..=size), ), - ( - vec(any::(), 0..=size), - vec(any_with::(size), 0..=size), - vec(any_with::(size), 0..=size), - ), + vec(any_with::(size), 0..=size), ( vec(any::(), 0..=size), vec(vec(any::(), 0..=size), 0..=size), @@ -1571,29 +1534,29 @@ impl Arbitrary for CompiledModuleMut { ), ( vec(any::(), 0..=size), - vec(any::(), 0..=size), vec(any_with::(size), 0..=size), ), ) .prop_map( |( (module_handles, struct_handles, function_handles), - (type_signatures, function_signatures, locals_signatures), + signatures, (identifiers, byte_array_pool, address_pool), - (struct_defs, field_defs, function_defs), + (struct_defs, function_defs), )| { CompiledModuleMut { module_handles, struct_handles, function_handles, - type_signatures, - function_signatures, - locals_signatures, + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], + signatures, identifiers, byte_array_pool, address_pool, struct_defs, - field_defs, function_defs, } }, @@ -1609,21 +1572,22 @@ impl CompiledModuleMut { IndexKind::ModuleHandle => self.module_handles.len(), IndexKind::StructHandle => self.struct_handles.len(), IndexKind::FunctionHandle => self.function_handles.len(), + IndexKind::FieldHandle => self.field_handles.len(), + IndexKind::StructDefInstantiation => self.struct_def_instantiations.len(), + IndexKind::FunctionInstantiation => self.function_instantiations.len(), + IndexKind::FieldInstantiation => self.field_instantiations.len(), IndexKind::StructDefinition => self.struct_defs.len(), - IndexKind::FieldDefinition => self.field_defs.len(), IndexKind::FunctionDefinition => self.function_defs.len(), - IndexKind::TypeSignature => self.type_signatures.len(), - IndexKind::FunctionSignature => self.function_signatures.len(), - IndexKind::LocalsSignature => self.locals_signatures.len(), + IndexKind::Signature => self.signatures.len(), IndexKind::Identifier => self.identifiers.len(), IndexKind::ByteArrayPool => self.byte_array_pool.len(), IndexKind::AddressPool => self.address_pool.len(), // XXX these two don't seem to belong here other @ IndexKind::LocalPool | other @ IndexKind::CodeDefinition - | other @ IndexKind::TypeParameter => { - unreachable!("invalid kind for count: {:?}", other) - } + | other @ IndexKind::FieldDefinition + | other @ IndexKind::TypeParameter + | other @ IndexKind::MemberCount => unreachable!("invalid kind for count: {:?}", other), } } @@ -1657,7 +1621,11 @@ impl CompiledModule { /// Returns the number of items of a specific `IndexKind`. pub fn kind_count(&self, kind: IndexKind) -> usize { precondition!(match kind { - IndexKind::LocalPool | IndexKind::CodeDefinition | IndexKind::TypeParameter => false, + IndexKind::LocalPool + | IndexKind::CodeDefinition + | IndexKind::FieldDefinition + | IndexKind::TypeParameter + | IndexKind::MemberCount => false, _ => true, }); self.as_inner().kind_count(kind) @@ -1688,9 +1656,9 @@ impl CompiledModule { struct_handles: inner.struct_handles, function_handles: inner.function_handles, - type_signatures: inner.type_signatures, - function_signatures: inner.function_signatures, - locals_signatures: inner.locals_signatures, + function_instantiations: inner.function_instantiations, + + signatures: inner.signatures, identifiers: inner.identifiers, byte_array_pool: inner.byte_array_pool, @@ -1705,19 +1673,20 @@ impl CompiledModule { pub fn empty_module() -> CompiledModuleMut { CompiledModuleMut { module_handles: vec![ModuleHandle { - address: AddressPoolIndex::new(0), - name: IdentifierIndex::new(0), + address: AddressPoolIndex(0), + name: IdentifierIndex(0), }], address_pool: vec![AccountAddress::default()], identifiers: vec![self_module_name().to_owned()], function_defs: vec![], struct_defs: vec![], - field_defs: vec![], struct_handles: vec![], function_handles: vec![], - type_signatures: vec![], - function_signatures: vec![], - locals_signatures: vec![LocalsSignature(vec![])], + field_handles: vec![], + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], + signatures: vec![Signature(vec![])], byte_array_pool: vec![], } } @@ -1732,58 +1701,46 @@ pub fn empty_module() -> CompiledModuleMut { pub fn basic_test_module() -> CompiledModuleMut { let mut m = empty_module(); - m.function_signatures.push(FunctionSignature { - return_types: vec![], - arg_types: vec![], - type_formals: vec![], - }); - m.function_handles.push(FunctionHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(m.identifiers.len() as u16), - signature: FunctionSignatureIndex::new(0), + module: ModuleHandleIndex(0), + name: IdentifierIndex(m.identifiers.len() as u16), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }); m.identifiers .push(Identifier::new("foo".to_string()).unwrap()); m.function_defs.push(FunctionDefinition { - function: FunctionHandleIndex::new(0), + function: FunctionHandleIndex(0), flags: 0, acquires_global_resources: vec![], code: CodeUnit { max_stack_size: 0, - locals: LocalsSignatureIndex::new(0), + locals: SignatureIndex(0), code: vec![], }, }); m.struct_handles.push(StructHandle { - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new(m.identifiers.len() as u16), + module: ModuleHandleIndex(0), + name: IdentifierIndex(m.identifiers.len() as u16), is_nominal_resource: false, - type_formals: vec![], + type_parameters: vec![], }); m.identifiers .push(Identifier::new("Bar".to_string()).unwrap()); m.struct_defs.push(StructDefinition { - struct_handle: StructHandleIndex::new(0), - field_information: StructFieldInformation::Declared { - field_count: 1, - fields: FieldDefinitionIndex::new(0), - }, - }); - - m.field_defs.push(FieldDefinition { - struct_: StructHandleIndex::new(0), - name: IdentifierIndex::new(m.identifiers.len() as u16), - signature: TypeSignatureIndex::new(0), + struct_handle: StructHandleIndex(0), + field_information: StructFieldInformation::Declared(vec![FieldDefinition { + name: IdentifierIndex(m.identifiers.len() as u16), + signature: TypeSignature(SignatureToken::U64), + }]), }); m.identifiers .push(Identifier::new("x".to_string()).unwrap()); - m.type_signatures.push(TypeSignature(SignatureToken::U64)); - m } @@ -1795,15 +1752,12 @@ pub fn dummy_procedure_module(code: Vec) -> CompiledModule { let mut fun_def = FunctionDefinition::default(); fun_def.code = code_unit; - module.function_signatures.push(FunctionSignature { - arg_types: vec![], - return_types: vec![], - type_formals: vec![], - }); let fun_handle = FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(0), - signature: FunctionSignatureIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }; module.function_handles.push(fun_handle); @@ -1816,12 +1770,7 @@ pub fn empty_script() -> CompiledScriptMut { let default_address = AccountAddress::new([3u8; AccountAddress::LENGTH]); let self_module_name = self_module_name().to_owned(); let main_name = Identifier::new("main").unwrap(); - let void_void_sig = FunctionSignature { - arg_types: vec![], - return_types: vec![], - type_formals: vec![], - }; - let no_args_no_locals = LocalsSignature(vec![]); + let signatures = vec![Signature(vec![])]; let self_module_handle = ModuleHandle { address: AddressPoolIndex(0), name: IdentifierIndex(0), @@ -1829,11 +1778,13 @@ pub fn empty_script() -> CompiledScriptMut { let main = FunctionHandle { module: ModuleHandleIndex(0), name: IdentifierIndex(1), - signature: FunctionSignatureIndex(0), + parameters: SignatureIndex(0), + return_: SignatureIndex(0), + type_parameters: vec![], }; let code = CodeUnit { max_stack_size: 1, - locals: LocalsSignatureIndex(0), + locals: SignatureIndex(0), code: vec![Bytecode::Ret], }; let main_def = FunctionDefinition { @@ -1847,9 +1798,9 @@ pub fn empty_script() -> CompiledScriptMut { struct_handles: vec![], function_handles: vec![main], - type_signatures: vec![], - function_signatures: vec![void_void_sig], - locals_signatures: vec![no_args_no_locals], + function_instantiations: vec![], + + signatures, identifiers: vec![self_module_name, main_name], byte_array_pool: vec![], diff --git a/language/vm/src/file_format_common.rs b/language/vm/src/file_format_common.rs index c74011fe6133..8d85c21ccfab 100644 --- a/language/vm/src/file_format_common.rs +++ b/language/vm/src/file_format_common.rs @@ -42,27 +42,17 @@ pub enum TableType { MODULE_HANDLES = 0x1, STRUCT_HANDLES = 0x2, FUNCTION_HANDLES = 0x3, - ADDRESS_POOL = 0x4, - IDENTIFIERS = 0x5, - BYTE_ARRAY_POOL = 0x6, - MAIN = 0x7, - STRUCT_DEFS = 0x8, - FIELD_DEFS = 0x9, - FUNCTION_DEFS = 0xA, - TYPE_SIGNATURES = 0xB, - FUNCTION_SIGNATURES = 0xC, - LOCALS_SIGNATURES = 0xD, -} - -/// Constants for signature kinds (type, function, locals). Those values start a signature blob. -#[rustfmt::skip] -#[allow(non_camel_case_types)] -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub enum SignatureType { - TYPE_SIGNATURE = 0x1, - FUNCTION_SIGNATURE = 0x2, - LOCAL_SIGNATURE = 0x3, + FUNCTION_INST = 0x4, + SIGNATURES = 0x5, + ADDRESS_POOL = 0x6, + IDENTIFIERS = 0x7, + BYTE_ARRAY_POOL = 0x8, + MAIN = 0x9, + STRUCT_DEFS = 0xA, + STRUCT_DEF_INST = 0xB, + FUNCTION_DEFS = 0xC, + FIELD_HANDLE = 0xD, + FIELD_INST = 0xE, } /// Constants for signature blob values. @@ -81,6 +71,7 @@ pub enum SerializedType { STRUCT = 0x8, TYPE_PARAMETER = 0x9, VECTOR = 0xA, + STRUCT_INST = 0xB, } #[rustfmt::skip] @@ -98,7 +89,7 @@ pub enum SerializedNominalResourceFlag { #[derive(Clone, Copy, Debug)] pub enum SerializedKind { ALL = 0x1, - UNRESTRICTED = 0x2, + COPYABLE = 0x2, RESOURCE = 0x3, } @@ -117,66 +108,75 @@ pub enum SerializedNativeStructFlag { #[repr(u8)] #[derive(Clone, Copy, Debug)] pub enum Opcodes { - POP = 0x01, - RET = 0x02, - BR_TRUE = 0x03, - BR_FALSE = 0x04, - BRANCH = 0x05, - LD_U64 = 0x06, - LD_ADDR = 0x07, - LD_TRUE = 0x08, - LD_FALSE = 0x09, - COPY_LOC = 0x0A, - MOVE_LOC = 0x0B, - ST_LOC = 0x0C, - MUT_BORROW_LOC = 0x0D, - IMM_BORROW_LOC = 0x0E, - MUT_BORROW_FIELD = 0x0F, - IMM_BORROW_FIELD = 0x10, - LD_BYTEARRAY = 0x11, - CALL = 0x12, - PACK = 0x13, - UNPACK = 0x14, - READ_REF = 0x15, - WRITE_REF = 0x16, - ADD = 0x17, - SUB = 0x18, - MUL = 0x19, - MOD = 0x1A, - DIV = 0x1B, - BIT_OR = 0x1C, - BIT_AND = 0x1D, - XOR = 0x1E, - OR = 0x1F, - AND = 0x20, - NOT = 0x21, - EQ = 0x22, - NEQ = 0x23, - LT = 0x24, - GT = 0x25, - LE = 0x26, - GE = 0x27, - ABORT = 0x28, - GET_TXN_GAS_UNIT_PRICE = 0x29, - GET_TXN_MAX_GAS_UNITS = 0x2A, - GET_GAS_REMAINING = 0x2B, - GET_TXN_SENDER = 0x2C, - EXISTS = 0x2D, - MUT_BORROW_GLOBAL = 0x2E, - IMM_BORROW_GLOBAL = 0x2F, - MOVE_FROM = 0x30, - MOVE_TO = 0x31, - GET_TXN_SEQUENCE_NUMBER = 0x32, - GET_TXN_PUBLIC_KEY = 0x33, - FREEZE_REF = 0x34, - // TODO: reshuffle once file format stabilizes - SHL = 0x35, - SHR = 0x36, - LD_U8 = 0x37, - LD_U128 = 0x38, - CAST_U8 = 0x39, - CAST_U64 = 0x3A, - CAST_U128 = 0x3B, + POP = 0x01, + RET = 0x02, + BR_TRUE = 0x03, + BR_FALSE = 0x04, + BRANCH = 0x05, + LD_U64 = 0x06, + LD_ADDR = 0x07, + LD_TRUE = 0x08, + LD_FALSE = 0x09, + COPY_LOC = 0x0A, + MOVE_LOC = 0x0B, + ST_LOC = 0x0C, + MUT_BORROW_LOC = 0x0D, + IMM_BORROW_LOC = 0x0E, + MUT_BORROW_FIELD = 0x0F, + IMM_BORROW_FIELD = 0x10, + LD_BYTEARRAY = 0x11, + CALL = 0x12, + PACK = 0x13, + UNPACK = 0x14, + READ_REF = 0x15, + WRITE_REF = 0x16, + ADD = 0x17, + SUB = 0x18, + MUL = 0x19, + MOD = 0x1A, + DIV = 0x1B, + BIT_OR = 0x1C, + BIT_AND = 0x1D, + XOR = 0x1E, + OR = 0x1F, + AND = 0x20, + NOT = 0x21, + EQ = 0x22, + NEQ = 0x23, + LT = 0x24, + GT = 0x25, + LE = 0x26, + GE = 0x27, + ABORT = 0x28, + GET_TXN_GAS_UNIT_PRICE = 0x29, + GET_TXN_MAX_GAS_UNITS = 0x2A, + GET_GAS_REMAINING = 0x2B, + GET_TXN_SENDER = 0x2C, + EXISTS = 0x2D, + MUT_BORROW_GLOBAL = 0x2E, + IMM_BORROW_GLOBAL = 0x2F, + MOVE_FROM = 0x30, + MOVE_TO = 0x31, + GET_TXN_SEQUENCE_NUMBER = 0x32, + GET_TXN_PUBLIC_KEY = 0x33, + FREEZE_REF = 0x34, + SHL = 0x35, + SHR = 0x36, + LD_U8 = 0x37, + LD_U128 = 0x38, + CAST_U8 = 0x39, + CAST_U64 = 0x3A, + CAST_U128 = 0x3B, + MUT_BORROW_FIELD_GENERIC = 0x3C, + IMM_BORROW_FIELD_GENERIC = 0x3D, + CALL_GENERIC = 0x3E, + PACK_GENERIC = 0x3F, + UNPACK_GENERIC = 0x40, + EXISTS_GENERIC = 0x41, + MUT_BORROW_GLOBAL_GENERIC = 0x42, + IMM_BORROW_GLOBAL_GENERIC = 0x43, + MOVE_FROM_GENERIC = 0x44, + MOVE_TO_GENERIC = 0x45, } /// Upper limit on the binary size diff --git a/language/vm/src/gas_schedule.rs b/language/vm/src/gas_schedule.rs index 6057dbd7a38e..c2ca7190e75c 100644 --- a/language/vm/src/gas_schedule.rs +++ b/language/vm/src/gas_schedule.rs @@ -7,8 +7,9 @@ //! operations or other native operations; the cost of each native operation will be returned by the //! native function itself. use crate::file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldDefinitionIndex, FunctionHandleIndex, - StructDefinitionIndex, NO_TYPE_ACTUALS, NUMBER_OF_NATIVE_FUNCTIONS, + AddressPoolIndex, ByteArrayPoolIndex, Bytecode, FieldHandleIndex, FieldInstantiationIndex, + FunctionHandleIndex, FunctionInstantiationIndex, StructDefInstantiationIndex, + StructDefinitionIndex, NUMBER_OF_NATIVE_FUNCTIONS, }; pub use crate::file_format_common::Opcodes; use libra_types::transaction::MAX_TRANSACTION_SIZE_IN_BYTES; @@ -209,18 +210,25 @@ pub fn instruction_key(instruction: &Bytecode) -> u8 { CopyLoc(_) => Opcodes::COPY_LOC, MoveLoc(_) => Opcodes::MOVE_LOC, StLoc(_) => Opcodes::ST_LOC, - Call(_, _) => Opcodes::CALL, - Pack(_, _) => Opcodes::PACK, - Unpack(_, _) => Opcodes::UNPACK, + Call(_) => Opcodes::CALL, + CallGeneric(_) => Opcodes::CALL_GENERIC, + Pack(_) => Opcodes::PACK, + PackGeneric(_) => Opcodes::PACK_GENERIC, + Unpack(_) => Opcodes::UNPACK, + UnpackGeneric(_) => Opcodes::UNPACK_GENERIC, ReadRef => Opcodes::READ_REF, WriteRef => Opcodes::WRITE_REF, FreezeRef => Opcodes::FREEZE_REF, MutBorrowLoc(_) => Opcodes::MUT_BORROW_LOC, ImmBorrowLoc(_) => Opcodes::IMM_BORROW_LOC, MutBorrowField(_) => Opcodes::MUT_BORROW_FIELD, + MutBorrowFieldGeneric(_) => Opcodes::MUT_BORROW_FIELD_GENERIC, ImmBorrowField(_) => Opcodes::IMM_BORROW_FIELD, - MutBorrowGlobal(_, _) => Opcodes::MUT_BORROW_GLOBAL, - ImmBorrowGlobal(_, _) => Opcodes::IMM_BORROW_GLOBAL, + ImmBorrowFieldGeneric(_) => Opcodes::IMM_BORROW_FIELD_GENERIC, + MutBorrowGlobal(_) => Opcodes::MUT_BORROW_GLOBAL, + MutBorrowGlobalGeneric(_) => Opcodes::MUT_BORROW_GLOBAL_GENERIC, + ImmBorrowGlobal(_) => Opcodes::IMM_BORROW_GLOBAL, + ImmBorrowGlobalGeneric(_) => Opcodes::IMM_BORROW_GLOBAL_GENERIC, Add => Opcodes::ADD, Sub => Opcodes::SUB, Mul => Opcodes::MUL, @@ -245,9 +253,12 @@ pub fn instruction_key(instruction: &Bytecode) -> u8 { GetTxnMaxGasUnits => Opcodes::GET_TXN_MAX_GAS_UNITS, GetGasRemaining => Opcodes::GET_GAS_REMAINING, GetTxnSenderAddress => Opcodes::GET_TXN_SENDER, - Exists(_, _) => Opcodes::EXISTS, - MoveFrom(_, _) => Opcodes::MOVE_FROM, - MoveToSender(_, _) => Opcodes::MOVE_TO, + Exists(_) => Opcodes::EXISTS, + ExistsGeneric(_) => Opcodes::EXISTS_GENERIC, + MoveFrom(_) => Opcodes::MOVE_FROM, + MoveFromGeneric(_) => Opcodes::MOVE_FROM_GENERIC, + MoveToSender(_) => Opcodes::MOVE_TO, + MoveToSenderGeneric(_) => Opcodes::MOVE_TO_GENERIC, GetTxnSequenceNumber => Opcodes::GET_TXN_SEQUENCE_NUMBER, GetTxnPublicKey => Opcodes::GET_TXN_PUBLIC_KEY, }; @@ -332,12 +343,17 @@ impl CostTable { // in the gas schedule for each instruction. let instrs = vec![ ( - MoveToSender(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MoveToSender(StructDefinitionIndex::new(0)), + GasCost::new(0, 0), + ), + ( + MoveToSenderGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), (GetTxnSenderAddress, GasCost::new(0, 0)), + (MoveFrom(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), ( - MoveFrom(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MoveFromGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), (BrTrue(0), GasCost::new(0, 0)), @@ -350,12 +366,14 @@ impl CostTable { (BitAnd, GasCost::new(0, 0)), (ReadRef, GasCost::new(0, 0)), (Sub, GasCost::new(0, 0)), + (MutBorrowField(FieldHandleIndex::new(0)), GasCost::new(0, 0)), ( - MutBorrowField(FieldDefinitionIndex::new(0)), + MutBorrowFieldGeneric(FieldInstantiationIndex::new(0)), GasCost::new(0, 0), ), + (ImmBorrowField(FieldHandleIndex::new(0)), GasCost::new(0, 0)), ( - ImmBorrowField(FieldDefinitionIndex::new(0)), + ImmBorrowFieldGeneric(FieldInstantiationIndex::new(0)), GasCost::new(0, 0), ), (Add, GasCost::new(0, 0)), @@ -379,14 +397,16 @@ impl CostTable { (Shr, GasCost::new(0, 0)), (Neq, GasCost::new(0, 0)), (Not, GasCost::new(0, 0)), + (Call(FunctionHandleIndex::new(0)), GasCost::new(0, 0)), ( - Call(FunctionHandleIndex::new(0), NO_TYPE_ACTUALS), + CallGeneric(FunctionInstantiationIndex::new(0)), GasCost::new(0, 0), ), (Le, GasCost::new(0, 0)), (Branch(0), GasCost::new(0, 0)), + (Unpack(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), ( - Unpack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + UnpackGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), (Or, GasCost::new(0, 0)), @@ -395,8 +415,9 @@ impl CostTable { (GetTxnGasUnitPrice, GasCost::new(0, 0)), (Mod, GasCost::new(0, 0)), (BrFalse(0), GasCost::new(0, 0)), + (Exists(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), ( - Exists(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + ExistsGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), (GetGasRemaining, GasCost::new(0, 0)), @@ -405,19 +426,28 @@ impl CostTable { (GetTxnSequenceNumber, GasCost::new(0, 0)), (FreezeRef, GasCost::new(0, 0)), ( - MutBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + MutBorrowGlobal(StructDefinitionIndex::new(0)), + GasCost::new(0, 0), + ), + ( + MutBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), + GasCost::new(0, 0), + ), + ( + ImmBorrowGlobal(StructDefinitionIndex::new(0)), GasCost::new(0, 0), ), ( - ImmBorrowGlobal(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + ImmBorrowGlobalGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), (Div, GasCost::new(0, 0)), (Eq, GasCost::new(0, 0)), (LdByteArray(ByteArrayPoolIndex::new(0)), GasCost::new(0, 0)), (Gt, GasCost::new(0, 0)), + (Pack(StructDefinitionIndex::new(0)), GasCost::new(0, 0)), ( - Pack(StructDefinitionIndex::new(0), NO_TYPE_ACTUALS), + PackGeneric(StructDefInstantiationIndex::new(0)), GasCost::new(0, 0), ), ]; diff --git a/language/vm/src/lib.rs b/language/vm/src/lib.rs index 5fbae722075f..fbff31cec98e 100644 --- a/language/vm/src/lib.rs +++ b/language/vm/src/lib.rs @@ -17,10 +17,8 @@ pub mod file_format; pub mod file_format_common; pub mod gas_schedule; pub mod internals; -pub mod printers; #[cfg(any(test, feature = "fuzzing"))] pub mod proptest_types; -pub mod resolver; pub mod serializer; pub mod transaction_metadata; pub mod views; @@ -36,18 +34,21 @@ pub enum IndexKind { ModuleHandle, StructHandle, FunctionHandle, + FieldHandle, + FunctionInstantiation, + FieldInstantiation, StructDefinition, - FieldDefinition, + StructDefInstantiation, FunctionDefinition, - TypeSignature, - FunctionSignature, - LocalsSignature, + FieldDefinition, + Signature, Identifier, ByteArrayPool, AddressPool, LocalPool, CodeDefinition, TypeParameter, + MemberCount, } impl IndexKind { @@ -60,17 +61,20 @@ impl IndexKind { ModuleHandle, StructHandle, FunctionHandle, + FieldHandle, + StructDefInstantiation, + FunctionInstantiation, + FieldInstantiation, StructDefinition, - FieldDefinition, FunctionDefinition, - TypeSignature, - FunctionSignature, - LocalsSignature, + FieldDefinition, + Signature, Identifier, AddressPool, LocalPool, CodeDefinition, TypeParameter, + MemberCount, ] } } @@ -83,18 +87,21 @@ impl fmt::Display for IndexKind { ModuleHandle => "module handle", StructHandle => "struct handle", FunctionHandle => "function handle", + FieldHandle => "field handle", + StructDefInstantiation => "struct instantiation", + FunctionInstantiation => "function instantiation", + FieldInstantiation => "field instantiation", StructDefinition => "struct definition", - FieldDefinition => "field definition", FunctionDefinition => "function definition", - TypeSignature => "type signature", - FunctionSignature => "function signature", - LocalsSignature => "locals signature", + FieldDefinition => "field definition", + Signature => "signature", Identifier => "identifier", ByteArrayPool => "byte_array pool", AddressPool => "address pool", LocalPool => "local pool", CodeDefinition => "code definition pool", TypeParameter => "type parameter", + MemberCount => "field offset", }; f.write_str(desc) diff --git a/language/vm/src/printers.rs b/language/vm/src/printers.rs deleted file mode 100644 index b7f67c7ef874..000000000000 --- a/language/vm/src/printers.rs +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright (c) The Libra Core Contributors -// SPDX-License-Identifier: Apache-2.0 - -use crate::file_format::*; -use anyhow::{bail, format_err, Result}; -use libra_types::account_address::AccountAddress; -use move_core_types::identifier::IdentStr; -use std::{collections::VecDeque, fmt}; - -// -// Display printing -// Display the top level compilation unit (CompiledScript and CompiledModule) in a more -// readable format. Essentially the printing resolves all table indexes and is a line by line -// for each table and with a reasonable indentation, e.g. -// ```text -// CompiledModule: { -// Struct Handles: [ -// ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000,] -// Field Handles: [ -// ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.item: Value,] -// Function Handles: [ -// ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.get(): Value, -// ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.new(Value): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000,] -// Struct Definitions: [ -// {public resource ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000 -// private ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.item: Value -// public ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.get(): Value -// static public ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.new(Value): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000},] -// Field Definitions: [ -// private ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.item: Value,] -// Function Definitions: [ -// public ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.get(): Value -// local(0): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000, -// local(1): &Value, -// local(2): Value, -// CopyLoc(0) -// BorrowField(ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.item: Value) -// StLoc(1) -// CopyLoc(1) -// ReadRef -// StLoc(2) -// MoveLoc(2) -// Ret, -// static public ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000.new(Value): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000 -// local(0): Value, -// local(1): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000, -// MoveLoc(0) -// Pack(ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000) -// StLoc(1) -// MoveLoc(1) -// Ret,] -// Signatures: [ -// Value, -// (): Value, -// (Value): ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000, -// ResourceBox@0x0000000000000000000000000000000000000000000000000000000000000000, -// &Value,] -// Strings: [ -// ResourceBox, -// item, -// get, -// new,] -// Addresses: [ -// 0x0000000000000000000000000000000000000000000000000000000000000000,] -// } -// ``` - -// Trait to access tables for both CompiledScript and CompiledModule. -// This is designed mainly for the printer -- public APIs should be based on the accessors in -// `access.rs`. -pub trait TableAccess { - fn get_field_def_at(&self, idx: FieldDefinitionIndex) -> Result<&FieldDefinition>; - - fn get_module_at(&self, idx: ModuleHandleIndex) -> Result<&ModuleHandle>; - fn get_struct_at(&self, idx: StructHandleIndex) -> Result<&StructHandle>; - fn get_function_at(&self, idx: FunctionHandleIndex) -> Result<&FunctionHandle>; - - fn get_identifier_at(&self, idx: IdentifierIndex) -> Result<&IdentStr>; - fn get_address_at(&self, idx: AddressPoolIndex) -> Result<&AccountAddress>; - fn get_type_signature_at(&self, idx: TypeSignatureIndex) -> Result<&TypeSignature>; - fn get_function_signature_at(&self, idx: FunctionSignatureIndex) -> Result<&FunctionSignature>; - fn get_locals_signature_at(&self, idx: LocalsSignatureIndex) -> Result<&LocalsSignature>; -} - -impl TableAccess for CompiledScriptMut { - fn get_field_def_at(&self, _idx: FieldDefinitionIndex) -> Result<&FieldDefinition> { - bail!("no field definitions in scripts"); - } - - fn get_module_at(&self, idx: ModuleHandleIndex) -> Result<&ModuleHandle> { - self.module_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad module handle index {}", idx)) - } - - fn get_struct_at(&self, idx: StructHandleIndex) -> Result<&StructHandle> { - self.struct_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad struct handle index {}", idx)) - } - - fn get_function_at(&self, idx: FunctionHandleIndex) -> Result<&FunctionHandle> { - self.function_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad function handle index {}", idx)) - } - - fn get_identifier_at(&self, idx: IdentifierIndex) -> Result<&IdentStr> { - self.identifiers - .get(idx.0 as usize) - .map(|x| x.as_ref()) - .ok_or_else(|| format_err!("bad string index {}", idx)) - } - - fn get_address_at(&self, idx: AddressPoolIndex) -> Result<&AccountAddress> { - self.address_pool - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad address index {}", idx)) - } - - fn get_type_signature_at(&self, idx: TypeSignatureIndex) -> Result<&TypeSignature> { - self.type_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } - - fn get_function_signature_at(&self, idx: FunctionSignatureIndex) -> Result<&FunctionSignature> { - self.function_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } - - fn get_locals_signature_at(&self, idx: LocalsSignatureIndex) -> Result<&LocalsSignature> { - self.locals_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } -} - -impl TableAccess for CompiledModuleMut { - fn get_field_def_at(&self, idx: FieldDefinitionIndex) -> Result<&FieldDefinition> { - self.field_defs - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad field definition index {}", idx)) - } - - fn get_module_at(&self, idx: ModuleHandleIndex) -> Result<&ModuleHandle> { - self.module_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad module handle index {}", idx)) - } - - fn get_struct_at(&self, idx: StructHandleIndex) -> Result<&StructHandle> { - self.struct_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad struct handle index {}", idx)) - } - - fn get_function_at(&self, idx: FunctionHandleIndex) -> Result<&FunctionHandle> { - self.function_handles - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad function handle index {}", idx)) - } - - fn get_identifier_at(&self, idx: IdentifierIndex) -> Result<&IdentStr> { - self.identifiers - .get(idx.0 as usize) - .map(|x| x.as_ref()) - .ok_or_else(|| format_err!("bad string index {}", idx)) - } - - fn get_address_at(&self, idx: AddressPoolIndex) -> Result<&AccountAddress> { - self.address_pool - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad address index {}", idx)) - } - - fn get_type_signature_at(&self, idx: TypeSignatureIndex) -> Result<&TypeSignature> { - self.type_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } - - fn get_function_signature_at(&self, idx: FunctionSignatureIndex) -> Result<&FunctionSignature> { - self.function_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } - - fn get_locals_signature_at(&self, idx: LocalsSignatureIndex) -> Result<&LocalsSignature> { - self.locals_signatures - .get(idx.0 as usize) - .ok_or_else(|| format_err!("bad signature index {}", idx)) - } -} - -impl fmt::Display for CompiledScript { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let inner = self.as_inner(); - write!(f, "CompiledScript: {{\nMain:\n\t")?; - display_function_definition(&inner.main, inner, f)?; - display_code(&inner.main.code, inner, "\n\t\t", f)?; - write!(f, "\nStruct Handles: [")?; - for struct_handle in &inner.struct_handles { - write!(f, "\n\t")?; - display_struct_handle(struct_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Module Handles: [")?; - for module_handle in &inner.module_handles { - write!(f, "\n\t")?; - display_module_handle(module_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Function Handles: [")?; - for function_handle in &inner.function_handles { - write!(f, "\n\t")?; - display_function_handle(function_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Type Signatures: [")?; - for signature in &inner.type_signatures { - write!(f, "\n\t")?; - display_type_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Function Signatures: [")?; - for signature in &inner.function_signatures { - write!(f, "\n\t")?; - display_function_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Locals Signatures: [")?; - for signature in &inner.locals_signatures { - write!(f, "\n\t")?; - display_locals_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Strings: [")?; - for string in &inner.identifiers { - write!(f, "\n\t{},", string)?; - } - writeln!(f, "]")?; - write!(f, "ByteArrays: [")?; - for byte_array in &inner.byte_array_pool { - write!(f, "\n\t")?; - display_byte_array(byte_array, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Addresses: [")?; - for address in &inner.address_pool { - write!(f, "\n\t")?; - display_address(address, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - writeln!(f, "}}") - } -} - -impl fmt::Display for CompiledModule { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let inner = self.as_inner(); - writeln!(f, "CompiledModule: {{")?; - write!(f, "Module Handles: [")?; - for module_handle in &inner.module_handles { - write!(f, "\n\t")?; - display_module_handle(module_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Struct Handles: [")?; - for struct_handle in &inner.struct_handles { - write!(f, "\n\t")?; - display_struct_handle(struct_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Function Handles: [")?; - for function_handle in &inner.function_handles { - write!(f, "\n\t")?; - display_function_handle(function_handle, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Struct Definitions: [")?; - for struct_def in &inner.struct_defs { - write!(f, "\n\t{{")?; - display_struct_definition(struct_def, inner, f)?; - match &struct_def.field_information { - StructFieldInformation::Native => write!(f, "native")?, - StructFieldInformation::Declared { - field_count, - fields, - } => { - let f_start_idx = *fields; - let f_end_idx = f_start_idx.0 as u16 + *field_count; - for idx in f_start_idx.0 as u16..f_end_idx { - let field_def = inner - .field_defs - .get(idx as usize) - .expect(&format!("bad field definition index {}", idx)[..]); - write!(f, "\n\t\t")?; - display_field_definition(field_def, inner, f)?; - } - } - } - write!(f, "}},")?; - } - writeln!(f, "]")?; - write!(f, "Field Definitions: [")?; - for field_def in &inner.field_defs { - write!(f, "\n\t")?; - display_field_definition(field_def, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Function Definitions: [")?; - for function_def in &inner.function_defs { - write!(f, "\n\t")?; - display_function_definition(function_def, inner, f)?; - if function_def.flags & CodeUnit::NATIVE == 0 { - display_code(&function_def.code, inner, "\n\t\t", f)?; - } - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Type Signatures: [")?; - for signature in &inner.type_signatures { - write!(f, "\n\t")?; - display_type_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Function Signatures: [")?; - for signature in &inner.function_signatures { - write!(f, "\n\t")?; - display_function_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Locals Signatures: [")?; - for signature in &inner.locals_signatures { - write!(f, "\n\t")?; - display_locals_signature(signature, inner, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Strings: [")?; - for string in &inner.identifiers { - write!(f, "\n\t{},", string)?; - } - writeln!(f, "]")?; - write!(f, "ByteArrays: [")?; - for byte_array in &inner.byte_array_pool { - write!(f, "\n\t")?; - display_byte_array(byte_array, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - write!(f, "Addresses: [")?; - for address in &inner.address_pool { - write!(f, "\n\t")?; - display_address(address, f)?; - write!(f, ",")?; - } - writeln!(f, "]")?; - writeln!(f, "}}") - } -} - -fn display_struct_handle( - struct_: &StructHandle, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - write!( - f, - "{} ", - if struct_.is_nominal_resource { - "resource" - } else { - "struct" - } - )?; - write!(f, "{}@", tables.get_identifier_at(struct_.name).unwrap())?; - display_module_handle(tables.get_module_at(struct_.module).unwrap(), tables, f) -} - -fn display_module_handle( - module: &ModuleHandle, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_address(tables.get_address_at(module.address).unwrap(), f)?; - write!(f, ".{}", tables.get_identifier_at(module.name).unwrap()) -} - -fn display_function_handle( - function: &FunctionHandle, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_module_handle(tables.get_module_at(function.module).unwrap(), tables, f)?; - write!(f, ".{}", tables.get_identifier_at(function.name).unwrap())?; - display_function_signature( - tables - .get_function_signature_at(function.signature) - .unwrap(), - tables, - f, - ) -} - -fn display_struct_definition( - struct_: &StructDefinition, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_struct_handle( - tables.get_struct_at(struct_.struct_handle).unwrap(), - tables, - f, - ) -} - -fn display_field_definition( - field: &FieldDefinition, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_struct_handle(tables.get_struct_at(field.struct_).unwrap(), tables, f)?; - write!(f, ".{}: ", tables.get_identifier_at(field.name).unwrap())?; - display_type_signature( - tables.get_type_signature_at(field.signature).unwrap(), - tables, - f, - ) -} - -fn display_function_definition( - function: &FunctionDefinition, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_function_flags(function.flags, f)?; - display_function_handle( - tables.get_function_at(function.function).unwrap(), - tables, - f, - ) -} - -fn display_code( - code: &CodeUnit, - tables: &T, - indentation: &str, - f: &mut fmt::Formatter, -) -> fmt::Result { - write!(f, "{}locals({}): ", indentation, code.locals,)?; - display_locals_signature( - tables.get_locals_signature_at(code.locals).unwrap(), - tables, - f, - )?; - write!(f, ",")?; - for bytecode in &code.code { - write!(f, "{}", indentation)?; - display_bytecode(bytecode, tables, f)?; - } - Ok(()) -} - -fn display_address(addr: &AccountAddress, f: &mut fmt::Formatter) -> fmt::Result { - let hex = format!("{:x}", addr); - let mut v: VecDeque = hex.chars().collect(); - while v.len() > 1 && v[0] == '0' { - v.pop_front(); - } - write!(f, "0x{}", v.into_iter().collect::()) -} - -fn display_byte_array(byte_array: &[u8], f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "0x{}", hex::encode(byte_array)) -} - -fn display_type_signature( - sig: &TypeSignature, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - display_signature_token(&sig.0, tables, f) -} - -fn display_function_signature( - sig: &FunctionSignature, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - let mut iter = sig.arg_types.iter().peekable(); - write!(f, "(")?; - while let Some(token) = iter.next() { - display_signature_token(token, tables, f)?; - if iter.peek().is_some() { - write!(f, ", ")?; - } - } - write!(f, "): ")?; - - let mut iter = sig.return_types.iter().peekable(); - write!(f, "(")?; - while let Some(token) = iter.next() { - display_signature_token(token, tables, f)?; - if iter.peek().is_some() { - write!(f, ", ")?; - } - } - write!(f, ")")?; - Ok(()) -} - -fn display_locals_signature( - sig: &LocalsSignature, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - let mut iter = sig.0.iter().peekable(); - while let Some(token) = iter.next() { - display_signature_token(token, tables, f)?; - if iter.peek().is_some() { - write!(f, ", ")?; - } - } - Ok(()) -} - -fn display_type_actuals( - types: &[SignatureToken], - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - if types.is_empty() { - return Ok(()); - } - write!(f, "<")?; - for (i, t) in types.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - display_signature_token(t, tables, f)?; - } - write!(f, ">") -} - -fn display_signature_token( - token: &SignatureToken, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - match token { - SignatureToken::Bool => write!(f, "Bool"), - SignatureToken::U8 => write!(f, "U8"), - SignatureToken::U64 => write!(f, "U64"), - SignatureToken::U128 => write!(f, "U128"), - SignatureToken::Address => write!(f, "Address"), - SignatureToken::Vector(ty) => { - write!(f, "Vector<")?; - display_signature_token(ty, tables, f)?; - write!(f, ">") - } - SignatureToken::Struct(idx, types) => { - display_struct_handle(tables.get_struct_at(*idx).unwrap(), tables, f)?; - display_type_actuals(&types, tables, f) - } - SignatureToken::Reference(token) => { - write!(f, "&")?; - display_signature_token(token, tables, f) - } - SignatureToken::MutableReference(token) => { - write!(f, "&mut ")?; - display_signature_token(token, tables, f) - } - SignatureToken::TypeParameter(idx) => write!(f, "T{}", idx), - } -} - -fn display_function_flags(flags: u8, f: &mut fmt::Formatter) -> fmt::Result { - if flags & CodeUnit::NATIVE != 0 { - write!(f, "native ")?; - } - if flags & CodeUnit::PUBLIC != 0 { - write!(f, "public ")?; - } - Ok(()) -} - -fn display_bytecode( - bytecode: &Bytecode, - tables: &T, - f: &mut fmt::Formatter, -) -> fmt::Result { - match bytecode { - Bytecode::LdAddr(idx) => { - write!(f, "LdAddr(")?; - display_address(tables.get_address_at(*idx).unwrap(), f)?; - write!(f, ")") - } - Bytecode::MutBorrowField(idx) => { - write!(f, "MutBorrowField(")?; - display_field_definition(tables.get_field_def_at(*idx).unwrap(), tables, f)?; - write!(f, ")") - } - Bytecode::ImmBorrowField(idx) => { - write!(f, "ImmBorrowField(")?; - display_field_definition(tables.get_field_def_at(*idx).unwrap(), tables, f)?; - write!(f, ")") - } - Bytecode::Call(idx, types_idx) => { - write!(f, "Call")?; - display_type_actuals( - &tables.get_locals_signature_at(*types_idx).unwrap().0, - tables, - f, - )?; - write!(f, "(")?; - display_function_handle(tables.get_function_at(*idx).unwrap(), tables, f)?; - write!(f, ")") - } - _ => write!(f, "{:?}", bytecode), - } -} diff --git a/language/vm/src/proptest_types.rs b/language/vm/src/proptest_types.rs index 968d2b684eb5..a0d3f631060a 100644 --- a/language/vm/src/proptest_types.rs +++ b/language/vm/src/proptest_types.rs @@ -4,16 +4,15 @@ //! Utilities for property-based testing. use crate::file_format::{ - AddressPoolIndex, CompiledModule, CompiledModuleMut, FieldDefinition, FieldDefinitionIndex, - FunctionHandle, FunctionSignatureIndex, IdentifierIndex, Kind, LocalsSignature, MemberCount, - ModuleHandle, ModuleHandleIndex, SignatureToken, StructDefinition, StructFieldInformation, - StructHandle, StructHandleIndex, TableIndex, TypeSignature, TypeSignatureIndex, + AddressPoolIndex, CompiledModule, CompiledModuleMut, FieldDefinition, FunctionDefinition, + FunctionHandle, IdentifierIndex, Kind, ModuleHandle, ModuleHandleIndex, SignatureToken, + StructDefinition, StructFieldInformation, StructHandle, StructHandleIndex, TableIndex, + TypeSignature, }; -use libra_proptest_helpers::GrowingSubset; use libra_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use proptest::{ - collection::{vec, SizeRange}, + collection::{btree_set, vec, SizeRange}, option, prelude::*, sample::Index as PropIndex, @@ -22,8 +21,10 @@ use proptest::{ mod functions; mod signature; +use crate::proptest_types::functions::{FnHandleMaterializeState, FunctionHandleGen}; use functions::{FnDefnMaterializeState, FunctionDefinitionGen}; -use signature::{FunctionSignatureGen, KindGen, SignatureTokenGen}; +use signature::{KindGen, SignatureTokenGen}; +use std::collections::{BTreeSet, HashSet}; /// Represents how large [`CompiledModule`] tables can be. pub type TableSize = u16; @@ -102,40 +103,52 @@ impl CompiledModuleStrategyGen { /// Create a `proptest` strategy for `CompiledModule` instances using this configuration. pub fn generate(self) -> impl Strategy { - // Base data -- everything points to this eventually. - let address_pool_strat = vec(any::(), 1..=self.size); - // This ensures that there are no empty ByteArrays - // TODO: Should we enable empty ByteArrays in Move, e.g. let byte_array = b""; - let byte_array_pool_strat = vec(vec(any::(), 0..=self.size), 1..=self.size); - let identifiers_strat = vec(any::(), 1..=self.size); - let type_signatures_strat = vec(SignatureTokenGen::strategy(), 1..=self.size); - // Ensure at least one owned non-struct type signature. - let owned_non_struct_strat = vec( - SignatureTokenGen::owned_non_struct_strategy(), - 1..=self.size, - ); - let owned_type_sigs_strat = vec(SignatureTokenGen::owned_strategy(), 1..=self.size); - let function_signatures_strat = vec( - FunctionSignatureGen::strategy( - self.member_count.clone(), - self.member_count.clone(), - self.member_count.clone(), - ), - 1..=self.size, + // + // leaf pool generator + // + let address_pool_strat = btree_set(any::(), 1..=self.size); + let byte_array_pool_strat = btree_set(vec(any::(), 0..=self.size), 1..=self.size); + let id_base_range = if self.size < 20 { 10 } else { 1 }; + let identifiers_strat = btree_set( + any::(), + id_base_range..=self.size + id_base_range, ); // The number of PropIndex instances in each tuple represents the number of pointers out // from an instance of that particular kind of node. + + // + // Module handle generator + // let module_handles_strat = vec(any::<(PropIndex, PropIndex)>(), 1..=self.size); + + // + // Struct generators + // let struct_handles_strat = vec( any::<(PropIndex, PropIndex, bool, Vec)>(), 1..=self.size, ); - let function_handles_strat = vec(any::<(PropIndex, PropIndex, PropIndex)>(), 1..=self.size); let struct_defs_strat = vec( StructDefinitionGen::strategy(self.member_count.clone()), 1..=self.size, ); + + // + // Functions generators + // + + // FunctionHandle will add to the Signature table + // FunctionDefinition will also add the following pool: + // FieldHandle, StructInstantiation, FunctionInstantiation, FieldInstantiation + let function_handles_strat = vec( + FunctionHandleGen::strategy( + self.member_count.clone(), + self.member_count.clone(), + self.member_count.clone(), + ), + 1..=self.size, + ); let function_defs_strat = vec( FunctionDefinitionGen::strategy( self.member_count.clone(), @@ -146,196 +159,145 @@ impl CompiledModuleStrategyGen { ), 1..=self.size, ); - // Note that prop_test only allows a tuple of length up to ten - // therefore, we need to treat the last two items as a pair to - // ensure we have less than 10 elements in the tuple. + + // Note that prop_test only allows a tuple of length up to 10 ( - address_pool_strat, - byte_array_pool_strat, - identifiers_strat, - type_signatures_strat, - owned_non_struct_strat, - owned_type_sigs_strat, - function_signatures_strat, - ( - module_handles_strat, - struct_handles_strat, - function_handles_strat, - ), - (struct_defs_strat, function_defs_strat), + (address_pool_strat, byte_array_pool_strat, identifiers_strat), + module_handles_strat, + (struct_handles_strat, struct_defs_strat), + (function_handles_strat, function_defs_strat), ) .prop_map( |( - address_pool, - byte_array_pool, - identifiers, - type_signatures, - owned_non_structs, - owned_type_sigs, - function_signatures, - (module_handles, struct_handles, function_handles), - (struct_defs, function_defs), + (address_pool_gen, byte_array_pool_gen, identifier_gens), + module_handles_gen, + (struct_handle_gens, struct_def_gens), + (function_handle_gens, function_def_gens), )| { + // + // leaf pools + let address_pool: Vec<_> = address_pool_gen.into_iter().collect(); let address_pool_len = address_pool.len(); + let identifiers: Vec<_> = identifier_gens.into_iter().collect(); let identifiers_len = identifiers.len(); + let byte_array_pool: Vec<_> = byte_array_pool_gen.into_iter().collect(); let byte_array_pool_len = byte_array_pool.len(); - let module_handles_len = module_handles.len(); - // StDefnMaterializeState adds one new handle for each definition, so the total - // number of struct handles is the sum of the number of generated struct - // handles (i.e. representing structs in external modules) and the number of - // internal ones. - let struct_handles_len = struct_handles.len() + struct_defs.len(); - // XXX FnDefnMaterializeState below adds more function signatures. This line - // means that no signatures generated later will be used by handles generated - // earlier. + // - // Instead, one could use function_signatures.len() + function_defs.len() to - // use signatures from later. - let function_signatures_len = function_signatures.len(); - // FnDefnMaterializeState below adds function handles equal to the number of - // function definitions. - let function_handles_len = function_handles.len() + function_defs.len(); - - let owned_type_sigs: Vec<_> = - SignatureTokenGen::map_materialize(owned_non_structs, struct_handles_len) - .chain(SignatureTokenGen::map_materialize( - owned_type_sigs, - struct_handles_len, - )) - .map(TypeSignature) - .collect(); - let owned_type_indexes = type_indexes(&owned_type_sigs); - - // Put the owned type signatures first so they're in the range - // 0..owned_type_sigs.len(). These are the signatures that will be used to pick - // field definition sigs from. - // Note that this doesn't result in a distribution that's spread out -- it - // would be nice to achieve that. - let type_signatures: Vec<_> = owned_type_sigs - .into_iter() - .chain( - SignatureTokenGen::map_materialize(type_signatures, struct_handles_len) - .map(TypeSignature), - ) - .collect(); - let function_signatures = function_signatures - .into_iter() - .map(|sig| sig.materialize(struct_handles_len)) - .collect(); - - let module_handles: Vec<_> = module_handles - .into_iter() - .map(|(address_idx, name_idx)| ModuleHandle { - address: AddressPoolIndex::new( - address_idx.index(address_pool_len) as TableIndex + // module handles + let mut module_handles_set = BTreeSet::new(); + for (address, name) in module_handles_gen { + module_handles_set.insert( + ModuleHandle { + address: AddressPoolIndex( + address.index(address_pool_len) as TableIndex ), - name: IdentifierIndex::new( - name_idx.index(identifiers_len) as TableIndex - ), - }) - .collect(); - - let struct_handles: Vec<_> = struct_handles - .into_iter() - .map( - |(module_idx, name_idx, is_nominal_resource, _type_formals)| { - StructHandle { - module: ModuleHandleIndex::new( - module_idx.index(module_handles_len) as TableIndex, - ), - name: IdentifierIndex::new( + name: IdentifierIndex(name.index(identifiers_len) as TableIndex), + }); + } + let module_handles: Vec<_> = module_handles_set.into_iter().collect(); + let module_handles_len = module_handles.len(); + + // + // struct handles + let mut struct_handles = vec![]; + if module_handles_len > 1 { + let mut struct_handles_set = BTreeSet::new(); + for (module_idx, name_idx, is_nominal_resource, _type_parameters) in + struct_handle_gens + { + let mut idx = module_idx.index(module_handles_len); + // 0 module handles are reserved for definitions and + // generated in the definition step + if idx == 0 { + idx += 1; + } + let module = ModuleHandleIndex(idx as TableIndex); + let name = + IdentifierIndex(name_idx.index(identifiers_len) as TableIndex); + if struct_handles_set.insert((module, name)) { + struct_handles.push(StructHandle { + module: ModuleHandleIndex(idx as TableIndex), + name: IdentifierIndex( name_idx.index(identifiers_len) as TableIndex ), is_nominal_resource, - // TODO: re-enable type formals gen when we rework prop tests + // TODO: enable type formals gen when we rework prop tests // for generics - type_formals: vec![], - } - }, - ) - .collect(); - - let function_handles: Vec<_> = function_handles - .into_iter() - .map(|(module_idx, name_idx, signature_idx)| FunctionHandle { - module: ModuleHandleIndex::new( - module_idx.index(module_handles_len) as TableIndex - ), - name: IdentifierIndex::new( - name_idx.index(identifiers_len) as TableIndex - ), - signature: FunctionSignatureIndex::new( - signature_idx.index(function_signatures_len) as TableIndex, - ), - }) - .collect(); + type_parameters: vec![], + }) + } + } + } - // Struct definitions also generate field definitions. - let mut state = StDefnMaterializeState { - identifiers_len, - owned_type_indexes, - struct_handles, - type_signatures, - // field_defs will be filled out by StructDefinitionGen::materialize - field_defs: vec![], - }; - let struct_defs: Vec<_> = struct_defs - .into_iter() - .map(|def| def.materialize(&mut state)) - .collect(); - - let StDefnMaterializeState { - struct_handles, - type_signatures, - field_defs, - .. - } = state; - assert_eq!(struct_handles_len, struct_handles.len()); - - // Definitions get generated at the end. But some of the other pools need to be - // involved here, so temporarily give up ownership to the state accumulators. - let mut state = FnDefnMaterializeState { - struct_handles_len, + // + // Struct definitions. + // Struct handles for the definitions are generated in this step + let mut state = StDefnMaterializeState::new(identifiers_len, struct_handles); + let mut struct_defs: Vec = vec![]; + for struct_def_gen in struct_def_gens { + if let Some(struct_def) = struct_def_gen.materialize(&mut state) { + struct_defs.push(struct_def); + } + } + let StDefnMaterializeState { struct_handles, .. } = state; + + // + // Function handles. Creates signatures. + let mut signatures = vec![]; + let mut function_handles: Vec = vec![]; + if module_handles_len > 1 { + let mut state = FnHandleMaterializeState::new( + module_handles_len, + identifiers_len, + struct_handles.len(), + ); + for function_handle_gen in function_handle_gens { + if let Some(function_handle) = + function_handle_gen.materialize(&mut state) + { + function_handles.push(function_handle); + } + } + signatures = state.signatures(); + } + + // + // Function Definitions + // Here we need pretty much everything if we are going to emit instructions. + // signatures and function handles + let mut state = FnDefnMaterializeState::new( address_pool_len, identifiers_len, byte_array_pool_len, - function_handles_len, - type_signatures_len: type_signatures.len(), - field_defs_len: field_defs.len(), - struct_defs_len: struct_defs.len(), - function_defs_len: function_defs.len(), - function_signatures, - // locals will be filled out by FunctionDefinitionGen::materialize - locals_signatures: vec![LocalsSignature(vec![])], - function_handles, - }; - - let function_defs = function_defs - .into_iter() - .map(|def| def.materialize(&mut state)) - .collect(); - - let FnDefnMaterializeState { - function_signatures, - locals_signatures, + struct_handles.len(), + struct_defs.len(), + signatures, function_handles, - .. - } = state; - assert_eq!(function_handles_len, function_handles.len()); + ); + let mut function_defs: Vec = vec![]; + for function_def_gen in function_def_gens { + if let Some(function_def) = function_def_gen.materialize(&mut state) { + function_defs.push(function_def); + } + } + let (signatures, function_handles) = state.return_tables(); - // Put it all together. + // Build a compiled module CompiledModuleMut { module_handles, struct_handles, function_handles, + field_handles: vec![], + + struct_def_instantiations: vec![], + function_instantiations: vec![], + field_instantiations: vec![], struct_defs, - field_defs, function_defs, - type_signatures, - function_signatures, - locals_signatures, + signatures, identifiers, byte_array_pool, @@ -348,51 +310,44 @@ impl CompiledModuleStrategyGen { } } +#[derive(Debug)] +struct TypeSignatureIndex(u16); + #[derive(Debug)] struct StDefnMaterializeState { identifiers_len: usize, - // Struct definitions need to be nonrecursive -- this is ensured by only picking signatures - // that either have no struct handle (represented as None), or have a handle less than the - // one for the definition currently being added. - owned_type_indexes: GrowingSubset, TypeSignatureIndex>, - // These get mutated by StructDefinitionGen. struct_handles: Vec, - field_defs: Vec, - type_signatures: Vec, + new_handles: BTreeSet<(ModuleHandleIndex, IdentifierIndex)>, } impl StDefnMaterializeState { - fn next_struct_handle(&self) -> StructHandleIndex { - StructHandleIndex::new(self.struct_handles.len() as TableIndex) - } - - fn add_struct_handle(&mut self, handle: StructHandle) -> StructHandleIndex { - self.struct_handles.push(handle); - StructHandleIndex::new((self.struct_handles.len() - 1) as TableIndex) + fn new(identifiers_len: usize, struct_handles: Vec) -> Self { + Self { + identifiers_len, + struct_handles, + new_handles: BTreeSet::new(), + } } - /// Adds field defs to the pool. Returns the number of fields added and the index of the first - /// field. - fn add_field_defs( - &mut self, - new_defs: impl IntoIterator, - ) -> (MemberCount, FieldDefinitionIndex) { - let old_len = self.field_defs.len(); - self.field_defs.extend(new_defs); - let new_len = self.field_defs.len(); - ( - (new_len - old_len) as MemberCount, - FieldDefinitionIndex::new(old_len as TableIndex), - ) + fn add_struct_handle(&mut self, handle: StructHandle) -> Option { + if self.new_handles.insert((handle.module, handle.name)) { + self.struct_handles.push(handle); + Some(StructHandleIndex((self.struct_handles.len() - 1) as u16)) + } else { + None + } } fn contains_nominal_resource(&self, signature: &SignatureToken) -> bool { use SignatureToken::*; match signature { - Struct(struct_handle_index, targs) => { + Struct(struct_handle_index) => { + self.struct_handles[struct_handle_index.0 as usize].is_nominal_resource + } + StructInstantiation(struct_handle_index, type_args) => { self.struct_handles[struct_handle_index.0 as usize].is_nominal_resource - || targs.iter().any(|t| self.contains_nominal_resource(t)) + || type_args.iter().any(|t| self.contains_nominal_resource(t)) } Vector(targ) => self.contains_nominal_resource(targ), Reference(token) | MutableReference(token) => self.contains_nominal_resource(token), @@ -407,7 +362,7 @@ struct StructDefinitionGen { // the is_nominal_resource field of generated struct handle is set to true if // either any of the fields contains a resource or self.is_nominal_resource is true is_nominal_resource: bool, - type_formals: Vec, + type_parameters: Vec, is_public: bool, field_defs: Option>, } @@ -420,132 +375,92 @@ impl StructDefinitionGen { // TODO: how to not hard-code the number? vec(KindGen::strategy(), 0..10), any::(), - // XXX 0..4 is the default member_count in CompiledModule -- is 0 (structs without - // fields) possible? + // XXX 0..4 is the default member_count in CompiledModule option::of(vec(FieldDefinitionGen::strategy(), member_count)), ) .prop_map( - |(name_idx, is_nominal_resource, _type_formals, is_public, field_defs)| Self { + |(name_idx, is_nominal_resource, _type_parameters, is_public, field_defs)| Self { name_idx, is_nominal_resource, // TODO: re-enable type formals gen once we rework prop tests for generics - type_formals: vec![], + type_parameters: vec![], is_public, field_defs, }, ) } - fn materialize(self, state: &mut StDefnMaterializeState) -> StructDefinition { - let sh_idx = state.next_struct_handle(); - state.owned_type_indexes.advance_to(&Some(sh_idx)); - let struct_handle = sh_idx; - + fn materialize(self, state: &mut StDefnMaterializeState) -> Option { + let mut field_names = HashSet::new(); + let mut fields = vec![]; match self.field_defs { - None => { - let is_nominal_resource = self.is_nominal_resource; - let handle = StructHandle { - // 0 represents the current module - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new( - self.name_idx.index(state.identifiers_len) as TableIndex - ), - is_nominal_resource, - type_formals: self - .type_formals - .into_iter() - .map(|kind| kind.materialize()) - .collect(), - }; - state.add_struct_handle(handle); - let field_information = StructFieldInformation::Native; - StructDefinition { - struct_handle, - field_information, + None => (), + Some(field_defs_gen) => { + for fd_gen in field_defs_gen { + let field = fd_gen.materialize(state); + if field_names.insert(field.name) { + fields.push(field); + } } } - Some(field_defs_gen) => { - // Each struct defines one or more fields. The collect() is to work around the - // borrow checker -- it's annoying. - let field_defs: Vec<_> = field_defs_gen - .into_iter() - .map(|field| field.materialize(sh_idx, state)) - .collect(); - let is_nominal_resource = self.is_nominal_resource - || field_defs.iter().any(|field| { - let field_sig = &state.type_signatures[field.signature.0 as usize].0; - state.contains_nominal_resource(field_sig) - }); - let (field_count, fields) = state.add_field_defs(field_defs); - - let handle = StructHandle { - // 0 represents the current module - module: ModuleHandleIndex::new(0), - name: IdentifierIndex::new( - self.name_idx.index(state.identifiers_len) as TableIndex - ), - is_nominal_resource, - type_formals: self - .type_formals - .into_iter() - .map(|kind| kind.materialize()) - .collect(), - }; - state.add_struct_handle(handle); - let field_information = StructFieldInformation::Declared { - field_count, - fields, - }; - StructDefinition { + }; + let is_nominal_resource = if fields.is_empty() { + self.is_nominal_resource + } else { + self.is_nominal_resource + || fields.iter().any(|field| { + let field_sig = &field.signature.0; + state.contains_nominal_resource(field_sig) + }) + }; + let handle = StructHandle { + // 0 represents the current module + module: ModuleHandleIndex(0), + name: IdentifierIndex(self.name_idx.index(state.identifiers_len) as TableIndex), + is_nominal_resource, + type_parameters: self + .type_parameters + .into_iter() + .map(|kind| kind.materialize()) + .collect(), + }; + state.add_struct_handle(handle).and_then(|struct_handle| { + if fields.is_empty() { + Some(StructDefinition { + struct_handle, + field_information: StructFieldInformation::Native, + }) + } else { + let field_information = StructFieldInformation::Declared(fields); + Some(StructDefinition { struct_handle, field_information, - } + }) } - } + }) } } #[derive(Clone, Debug)] struct FieldDefinitionGen { name_idx: PropIndex, - signature_idx: PropIndex, - // XXX flags? + signature_gen: SignatureTokenGen, } impl FieldDefinitionGen { fn strategy() -> impl Strategy { - (any::(), any::()).prop_map(|(name_idx, signature_idx)| Self { - name_idx, - signature_idx, - }) + (any::(), SignatureTokenGen::atom_strategy()).prop_map( + |(name_idx, signature_gen)| Self { + name_idx, + signature_gen, + }, + ) } - fn materialize( - self, - sh_idx: StructHandleIndex, - state: &StDefnMaterializeState, - ) -> FieldDefinition { + fn materialize(self, state: &StDefnMaterializeState) -> FieldDefinition { FieldDefinition { - struct_: sh_idx, - name: IdentifierIndex::new(self.name_idx.index(state.identifiers_len) as TableIndex), - signature: *state.owned_type_indexes.pick_value(&self.signature_idx), + name: IdentifierIndex(self.name_idx.index(state.identifiers_len) as TableIndex), + signature: TypeSignature(self.signature_gen.materialize(state.struct_handles.len())), } } } - -fn type_indexes<'a>( - signatures: impl IntoIterator, -) -> GrowingSubset, TypeSignatureIndex> { - signatures - .into_iter() - .enumerate() - .map(|(idx, signature)| { - // Any signatures that don't have a struct handle in them can always be picked. - // None is less than Some(0) so set those to None. - ( - signature.0.struct_index(), - TypeSignatureIndex::new(idx as TableIndex), - ) - }) - .collect() -} diff --git a/language/vm/src/proptest_types/functions.rs b/language/vm/src/proptest_types/functions.rs index d9d6e8ebc9a5..14834bd50a15 100644 --- a/language/vm/src/proptest_types/functions.rs +++ b/language/vm/src/proptest_types/functions.rs @@ -3,13 +3,13 @@ use crate::{ file_format::{ - AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, CodeUnit, FieldDefinitionIndex, - FunctionDefinition, FunctionHandle, FunctionHandleIndex, FunctionSignature, - FunctionSignatureIndex, IdentifierIndex, LocalIndex, LocalsSignature, LocalsSignatureIndex, - ModuleHandleIndex, StructDefinitionIndex, TableIndex, NO_TYPE_ACTUALS, + AddressPoolIndex, ByteArrayPoolIndex, Bytecode, CodeOffset, CodeUnit, FieldHandle, + FieldHandleIndex, FunctionDefinition, FunctionHandle, FunctionHandleIndex, IdentifierIndex, + LocalIndex, ModuleHandleIndex, Signature, SignatureIndex, StructDefinitionIndex, + TableIndex, }, proptest_types::{ - signature::{FunctionSignatureGen, SignatureTokenGen}, + signature::{SignatureGen, SignatureTokenGen}, TableSize, }, }; @@ -18,54 +18,248 @@ use proptest::{ prelude::*, sample::{select, Index as PropIndex}, }; +use std::{ + collections::{BTreeSet, HashMap, HashSet}, + hash::Hash, +}; + +#[derive(Debug, Default)] +struct SignatureState { + signatures: Vec, + signature_map: HashMap, +} + +impl SignatureState { + fn new(signatures: Vec) -> Self { + let mut state = Self::default(); + for sig in signatures { + state.add_signature(sig); + } + state + } + + fn signatures(self) -> Vec { + self.signatures + } + + fn add_signature(&mut self, sig: Signature) -> SignatureIndex { + precondition!(self.signatures.len() < TableSize::max_value() as usize); + if let Some(idx) = self.signature_map.get(&sig) { + return *idx; + } + let idx = SignatureIndex(self.signatures.len() as u16); + self.signatures.push(sig.clone()); + self.signature_map.insert(sig, idx); + idx + } +} + +#[derive(Debug, Default)] +#[allow(unused)] +struct FieldHandleState { + field_handles: Vec, + field_map: HashMap, +} + +impl FieldHandleState { + #[allow(unused)] + pub fn field_handles(self) -> Vec { + self.field_handles + } + + #[allow(unused)] + fn add_field_handle(&mut self, fh: FieldHandle) -> Option { + precondition!(self.field_handles.len() < TableSize::max_value() as usize); + if let Some(idx) = self.field_map.get(&fh) { + return Some(*idx); + } + let idx = FieldHandleIndex(self.field_handles.len() as u16); + self.field_handles.push(fh.clone()); + self.field_map.insert(fh, idx); + Some(idx) + } +} + +#[derive(Debug, Default)] +#[allow(unused)] +struct InstantiationState +where + T: Eq + Clone + Hash, +{ + instantiations: Vec, + instantiation_map: HashMap, +} + +impl InstantiationState +where + T: Eq + Clone + Hash, +{ + #[allow(unused)] + pub fn instantiations(self) -> Vec { + self.instantiations + } + + #[allow(unused)] + fn add_instantiation(&mut self, inst: T) -> TableIndex { + precondition!(self.instantiations.len() < TableSize::max_value() as usize); + if let Some(idx) = self.instantiation_map.get(&inst) { + return *idx; + } + let idx = self.instantiations.len() as TableIndex; + self.instantiations.push(inst.clone()); + self.instantiation_map.insert(inst, idx); + idx + } +} + +/// Represents state required to materialize final data structures for function definitions. +#[derive(Debug)] +pub struct FnHandleMaterializeState { + module_handles_len: usize, + identifiers_len: usize, + struct_handles_len: usize, + signatures: SignatureState, + function_handles: HashSet<(ModuleHandleIndex, IdentifierIndex)>, +} + +impl FnHandleMaterializeState { + pub fn new( + module_handles_len: usize, + identifiers_len: usize, + struct_handles_len: usize, + ) -> Self { + Self { + module_handles_len, + identifiers_len, + struct_handles_len, + signatures: SignatureState::default(), + function_handles: HashSet::new(), + } + } + + pub fn signatures(self) -> Vec { + self.signatures.signatures() + } + + fn add_signature(&mut self, sig: Signature) -> SignatureIndex { + self.signatures.add_signature(sig) + } +} + +#[derive(Clone, Debug)] +pub struct FunctionHandleGen { + module: PropIndex, + name: PropIndex, + parameters: SignatureGen, + return_: SignatureGen, +} + +impl FunctionHandleGen { + pub fn strategy( + param_count: impl Into, + return_count: impl Into, + _kind_count: impl Into, + ) -> impl Strategy { + let return_count = return_count.into(); + let param_count = param_count.into(); + ( + any::(), + any::(), + SignatureGen::strategy(param_count), + SignatureGen::strategy(return_count), + ) + .prop_map(|(module, name, parameters, return_)| Self { + module, + name, + parameters, + return_, + }) + } + + pub fn materialize(self, state: &mut FnHandleMaterializeState) -> Option { + let mod_ = ModuleHandleIndex(self.module.index(state.module_handles_len) as TableIndex); + let mod_idx = if mod_.0 == 0 { + ModuleHandleIndex(1) + } else { + mod_ + }; + let iden_idx = IdentifierIndex(self.name.index(state.identifiers_len) as TableIndex); + if state.function_handles.contains(&(mod_idx, iden_idx)) { + return None; + } + state.function_handles.insert((mod_idx, iden_idx)); + let parameters = self.parameters.materialize(state.struct_handles_len); + let params_idx = state.add_signature(parameters); + let return_ = self.return_.materialize(state.struct_handles_len); + let return_idx = state.add_signature(return_); + Some(FunctionHandle { + module: mod_idx, + name: iden_idx, + parameters: params_idx, + return_: return_idx, + // TODO: re-enable type formals gen when we rework prop tests for generics + type_parameters: vec![], + }) + } +} /// Represents state required to materialize final data structures for function definitions. #[derive(Debug)] pub struct FnDefnMaterializeState { - pub struct_handles_len: usize, - pub address_pool_len: usize, - pub identifiers_len: usize, - pub byte_array_pool_len: usize, - pub type_signatures_len: usize, - pub field_defs_len: usize, - pub struct_defs_len: usize, - pub function_defs_len: usize, - // This is the final length of function_handles, after all the definitions add their own - // handles. - pub function_handles_len: usize, - // These get mutated by `FunctionDefinitionGen`. - pub function_signatures: Vec, - pub locals_signatures: Vec, - pub function_handles: Vec, + address_pool_len: usize, + identifiers_len: usize, + byte_array_pool_len: usize, + struct_handles_len: usize, + struct_defs_len: usize, + signatures: SignatureState, + function_handles: Vec, + def_function_handles: HashSet<(ModuleHandleIndex, IdentifierIndex)>, + field_handles: FieldHandleState, } impl FnDefnMaterializeState { - #[inline] - fn add_function_signature(&mut self, sig: FunctionSignature) -> FunctionSignatureIndex { - precondition!(self.function_signatures.len() < TableSize::max_value() as usize); - self.function_signatures.push(sig); - FunctionSignatureIndex::new((self.function_signatures.len() - 1) as TableIndex) + pub fn new( + address_pool_len: usize, + identifiers_len: usize, + byte_array_pool_len: usize, + struct_handles_len: usize, + struct_defs_len: usize, + signatures: Vec, + function_handles: Vec, + ) -> Self { + Self { + address_pool_len, + identifiers_len, + byte_array_pool_len, + struct_handles_len, + struct_defs_len, + signatures: SignatureState::new(signatures), + function_handles, + def_function_handles: HashSet::new(), + field_handles: FieldHandleState::default(), + } + } + + pub fn return_tables(self) -> (Vec, Vec) { + (self.signatures.signatures(), self.function_handles) } - #[inline] - fn add_locals_signature(&mut self, sig: LocalsSignature) -> LocalsSignatureIndex { - precondition!(self.locals_signatures.len() < TableSize::max_value() as usize); - self.locals_signatures.push(sig); - LocalsSignatureIndex::new((self.locals_signatures.len() - 1) as TableIndex) + fn add_signature(&mut self, sig: Signature) -> SignatureIndex { + self.signatures.add_signature(sig) } - #[inline] fn add_function_handle(&mut self, handle: FunctionHandle) -> FunctionHandleIndex { precondition!(self.function_handles.len() < TableSize::max_value() as usize); self.function_handles.push(handle); - FunctionHandleIndex::new((self.function_handles.len() - 1) as TableIndex) + FunctionHandleIndex((self.function_handles.len() - 1) as TableIndex) } } #[derive(Clone, Debug)] pub struct FunctionDefinitionGen { name: PropIndex, - signature: FunctionSignatureGen, + parameters: SignatureGen, + return_: SignatureGen, is_public: bool, acquires: Vec, code: CodeUnitGen, @@ -75,7 +269,7 @@ impl FunctionDefinitionGen { pub fn strategy( return_count: impl Into, arg_count: impl Into, - kind_count: impl Into, + _kind_count: impl Into, acquires_count: impl Into, code_len: impl Into, ) -> impl Strategy { @@ -83,46 +277,59 @@ impl FunctionDefinitionGen { let arg_count = arg_count.into(); ( any::(), - FunctionSignatureGen::strategy(return_count, arg_count.clone(), kind_count.into()), + SignatureGen::strategy(arg_count.clone()), + SignatureGen::strategy(return_count), any::(), vec(any::(), acquires_count.into()), CodeUnitGen::strategy(arg_count, code_len), ) - .prop_map(|(name, signature, is_public, acquires, code)| Self { - name, - signature, - is_public, - acquires, - code, - }) + .prop_map( + |(name, parameters, return_, is_public, acquires, code)| Self { + name, + parameters, + return_, + is_public, + acquires, + code, + }, + ) } - pub fn materialize(self, state: &mut FnDefnMaterializeState) -> FunctionDefinition { + pub fn materialize(self, state: &mut FnDefnMaterializeState) -> Option { // This precondition should never fail because the table size cannot be greater // than TableSize::max_value() - checked_precondition!( - state.function_signatures.len() < TableSize::max_value() as usize - && state.locals_signatures.len() < TableSize::max_value() as usize - && state.function_handles.len() < TableSize::max_value() as usize - ); - let signature = self.signature.materialize(state.struct_handles_len); + let iden_idx = IdentifierIndex(self.name.index(state.identifiers_len) as TableIndex); + if state + .def_function_handles + .contains(&(ModuleHandleIndex(0), iden_idx)) + { + return None; + } + state + .def_function_handles + .insert((ModuleHandleIndex(0), iden_idx)); + let parameters = self.parameters.materialize(state.struct_handles_len); + let params_idx = state.add_signature(parameters); + let return_ = self.return_.materialize(state.struct_handles_len); + let return_idx = state.add_signature(return_); let handle = FunctionHandle { - // 0 represents the current module - module: ModuleHandleIndex::new(0), - // XXX need to guarantee uniqueness of names? - name: IdentifierIndex::new(self.name.index(state.identifiers_len) as TableIndex), - signature: state.add_function_signature(signature), + module: ModuleHandleIndex(0), + name: iden_idx, + parameters: params_idx, + return_: return_idx, + type_parameters: vec![], }; let function_handle = state.add_function_handle(handle); - let acquires_global_resources = self - .acquires - .into_iter() - .map(|idx| StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex)) - .collect(); - FunctionDefinition { + let mut acquires_set = BTreeSet::new(); + for acquire in self.acquires { + acquires_set.insert(StructDefinitionIndex( + acquire.index(state.struct_defs_len) as TableIndex + )); + } + let acquires_global_resources = acquires_set.into_iter().collect(); + Some(FunctionDefinition { function: function_handle, - // XXX is this even correct? flags: if self.is_public { CodeUnit::PUBLIC } else { @@ -131,7 +338,7 @@ impl FunctionDefinitionGen { }, acquires_global_resources, code: self.code.materialize(state), - } + }) } } @@ -157,31 +364,23 @@ impl CodeUnitGen { } fn materialize(self, state: &mut FnDefnMaterializeState) -> CodeUnit { - precondition!(state.locals_signatures.len() < TableSize::max_value() as usize); - let locals_signature = LocalsSignature( + let locals_signature = Signature( self.locals_signature .into_iter() .map(|sig| sig.materialize(state.struct_handles_len)) .collect(), ); - // Not all bytecodes will be successfully materialized -- count how many will. - let code_len = self - .code - .iter() - .filter(|code| code.will_materialize(state, &locals_signature)) - .count(); - - let code = self - .code - .into_iter() - .filter_map(|code| code.materialize(state, code_len, &locals_signature)) - .collect(); + let mut code = vec![]; + for bytecode_gen in self.code { + if let Some(bytecode) = bytecode_gen.materialize(state, code.len(), &locals_signature) { + code.push(bytecode) + } + } CodeUnit { max_stack_size: 0, - locals: state.add_locals_signature(locals_signature), - // XXX actually generate code + locals: state.add_signature(locals_signature), code, } } @@ -195,15 +394,25 @@ enum BytecodeGen { LdAddr(PropIndex), LdByteArray(PropIndex), MutBorrowField(PropIndex), + MutBorrowFieldGeneric(PropIndex), ImmBorrowField(PropIndex), - Call(PropIndex, PropIndex), - Pack(PropIndex, PropIndex), - Unpack(PropIndex, PropIndex), - Exists(PropIndex, PropIndex), - MutBorrowGlobal(PropIndex, PropIndex), - ImmBorrowGlobal(PropIndex, PropIndex), - MoveFrom(PropIndex, PropIndex), - MoveToSender(PropIndex, PropIndex), + ImmBorrowFieldGeneric(PropIndex), + Call(PropIndex), + CallGeneric(PropIndex), + Pack(PropIndex), + PackGeneric(PropIndex), + Unpack(PropIndex), + UnpackGeneric(PropIndex), + Exists(PropIndex), + ExistsGeneric(PropIndex), + MutBorrowGlobal(PropIndex), + MutBorrowGlobalGeneric(PropIndex), + ImmBorrowGlobal(PropIndex), + ImmBorrowGlobalGeneric(PropIndex), + MoveFrom(PropIndex), + MoveFromGeneric(PropIndex), + MoveToSender(PropIndex), + MoveToSenderGeneric(PropIndex), BrTrue(PropIndex), BrFalse(PropIndex), Branch(PropIndex), @@ -225,18 +434,25 @@ impl BytecodeGen { any::().prop_map(LdAddr), any::().prop_map(LdByteArray), any::().prop_map(ImmBorrowField), + any::().prop_map(ImmBorrowFieldGeneric), any::().prop_map(MutBorrowField), - (any::(), any::(),).prop_map(|(idx, types)| Call(idx, types)), - (any::(), any::(),).prop_map(|(idx, types)| Pack(idx, types)), - (any::(), any::(),).prop_map(|(idx, types)| Unpack(idx, types)), - (any::(), any::(),).prop_map(|(idx, types)| Exists(idx, types)), - (any::(), any::(),) - .prop_map(|(idx, types)| ImmBorrowGlobal(idx, types)), - (any::(), any::(),) - .prop_map(|(idx, types)| MutBorrowGlobal(idx, types)), - (any::(), any::(),).prop_map(|(idx, types)| MoveFrom(idx, types)), - (any::(), any::(),) - .prop_map(|(idx, types)| MoveToSender(idx, types)), + any::().prop_map(MutBorrowFieldGeneric), + any::().prop_map(Call), + any::().prop_map(CallGeneric), + any::().prop_map(Pack), + any::().prop_map(PackGeneric), + any::().prop_map(Unpack), + any::().prop_map(UnpackGeneric), + any::().prop_map(Exists), + any::().prop_map(ExistsGeneric), + any::().prop_map(ImmBorrowGlobal), + any::().prop_map(ImmBorrowGlobalGeneric), + any::().prop_map(MutBorrowGlobal), + any::().prop_map(MutBorrowGlobalGeneric), + any::().prop_map(MoveFrom), + any::().prop_map(MoveFromGeneric), + any::().prop_map(MoveToSender), + any::().prop_map(MoveToSenderGeneric), any::().prop_map(BrTrue), any::().prop_map(BrFalse), any::().prop_map(Branch), @@ -248,107 +464,82 @@ impl BytecodeGen { ] } - /// Whether this code will be materialized into a Some(bytecode). - fn will_materialize( - &self, - state: &FnDefnMaterializeState, - locals_signature: &LocalsSignature, - ) -> bool { - // This method should remain in sync with the `None` below. - use BytecodeGen::*; - - match self { - MutBorrowField(_) | ImmBorrowField(_) => state.field_defs_len != 0, - CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) => { - !locals_signature.is_empty() - } - _ => true, - } - } - fn materialize( self, state: &FnDefnMaterializeState, code_len: usize, - locals_signature: &LocalsSignature, + locals_signature: &Signature, ) -> Option { - // This method returns an Option because some bytecodes cannot be represented if - // some tables are empty. - // - // Once more sensible function bodies are generated this will probably have to start using - // prop_flat_map anyway, so revisit this then. - let bytecode = match self { BytecodeGen::Simple(bytecode) => bytecode, - BytecodeGen::LdAddr(idx) => Bytecode::LdAddr(AddressPoolIndex::new( + BytecodeGen::LdAddr(idx) => Bytecode::LdAddr(AddressPoolIndex( idx.index(state.address_pool_len) as TableIndex, )), - BytecodeGen::LdByteArray(idx) => Bytecode::LdByteArray(ByteArrayPoolIndex::new( + BytecodeGen::LdByteArray(idx) => Bytecode::LdByteArray(ByteArrayPoolIndex( idx.index(state.byte_array_pool_len) as TableIndex, )), - BytecodeGen::MutBorrowField(idx) => { - // Again, once meaningful bytecodes are generated this won't actually be a - // possibility since it would be impossible to load a field from a struct that - // doesn't have any. - if state.field_defs_len == 0 { + BytecodeGen::MutBorrowField(_idx) => { + return None; + } + BytecodeGen::MutBorrowFieldGeneric(_idx) => { + return None; + } + BytecodeGen::ImmBorrowField(_idx) => { + return None; + } + BytecodeGen::ImmBorrowFieldGeneric(_idx) => { + return None; + } + BytecodeGen::Call(idx) => Bytecode::Call(FunctionHandleIndex( + idx.index(state.function_handles.len()) as TableIndex, + )), + BytecodeGen::CallGeneric(_idx) => return None, + BytecodeGen::Pack(idx) => Bytecode::Pack(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::PackGeneric(_idx) => return None, + BytecodeGen::Unpack(idx) => Bytecode::Unpack(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::UnpackGeneric(_idx) => return None, + BytecodeGen::Exists(idx) => Bytecode::Exists(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::ExistsGeneric(_idx) => return None, + BytecodeGen::ImmBorrowGlobal(idx) => Bytecode::ImmBorrowGlobal(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::ImmBorrowGlobalGeneric(_idx) => return None, + BytecodeGen::MutBorrowGlobal(idx) => Bytecode::MutBorrowGlobal(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::MutBorrowGlobalGeneric(_idx) => return None, + BytecodeGen::MoveFrom(idx) => Bytecode::MoveFrom(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::MoveFromGeneric(_idx) => return None, + BytecodeGen::MoveToSender(idx) => Bytecode::MoveToSender(StructDefinitionIndex( + idx.index(state.struct_defs_len) as TableIndex, + )), + BytecodeGen::MoveToSenderGeneric(_idx) => return None, + BytecodeGen::BrTrue(idx) => { + if code_len == 0 { + return None; + } + Bytecode::BrTrue(idx.index(code_len) as CodeOffset) + } + BytecodeGen::BrFalse(idx) => { + if code_len == 0 { return None; } - Bytecode::MutBorrowField(FieldDefinitionIndex::new( - idx.index(state.field_defs_len) as TableIndex - )) + Bytecode::BrFalse(idx.index(code_len) as CodeOffset) } - BytecodeGen::ImmBorrowField(idx) => { - // Same situation as above - if state.field_defs_len == 0 { + BytecodeGen::Branch(idx) => { + if code_len == 0 { return None; } - Bytecode::ImmBorrowField(FieldDefinitionIndex::new( - idx.index(state.field_defs_len) as TableIndex - )) + Bytecode::Branch(idx.index(code_len) as CodeOffset) } - BytecodeGen::Call(idx, _types_idx) => Bytecode::Call( - FunctionHandleIndex::new(idx.index(state.function_handles_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::Pack(idx, _types_idx) => Bytecode::Pack( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::Unpack(idx, _types_idx) => Bytecode::Unpack( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::Exists(idx, _types_idx) => Bytecode::Exists( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::ImmBorrowGlobal(idx, _types_idx) => Bytecode::ImmBorrowGlobal( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::MutBorrowGlobal(idx, _types_idx) => Bytecode::MutBorrowGlobal( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::MoveFrom(idx, _types_idx) => Bytecode::MoveFrom( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::MoveToSender(idx, _types_idx) => Bytecode::MoveToSender( - StructDefinitionIndex::new(idx.index(state.struct_defs_len) as TableIndex), - // TODO: generate random index to type actuals once generics is fully implemented - NO_TYPE_ACTUALS, - ), - BytecodeGen::BrTrue(idx) => Bytecode::BrTrue(idx.index(code_len) as CodeOffset), - BytecodeGen::BrFalse(idx) => Bytecode::BrFalse(idx.index(code_len) as CodeOffset), - BytecodeGen::Branch(idx) => Bytecode::Branch(idx.index(code_len) as CodeOffset), BytecodeGen::CopyLoc(idx) => { if locals_signature.is_empty() { return None; diff --git a/language/vm/src/proptest_types/signature.rs b/language/vm/src/proptest_types/signature.rs index 9cd781e3ead9..4befaa7a8421 100644 --- a/language/vm/src/proptest_types/signature.rs +++ b/language/vm/src/proptest_types/signature.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::file_format::{ - FunctionSignature, Kind, SignatureToken, StructHandleIndex, TableIndex, TypeParameterIndex, + Kind, Signature, SignatureToken, StructHandleIndex, TableIndex, TypeParameterIndex, }; use proptest::{ collection::{vec, SizeRange}, @@ -13,14 +13,14 @@ use proptest::{ #[derive(Clone, Debug)] pub enum KindGen { Resource, - Unrestricted, + Copyable, } impl KindGen { pub fn strategy() -> impl Strategy { use KindGen::*; - static KINDS: &[KindGen] = &[Resource, Unrestricted]; + static KINDS: &[KindGen] = &[Resource, Copyable]; select(KINDS) } @@ -28,49 +28,23 @@ impl KindGen { pub fn materialize(self) -> Kind { match self { KindGen::Resource => Kind::Resource, - KindGen::Unrestricted => Kind::Unrestricted, + KindGen::Copyable => Kind::Copyable, } } } #[derive(Clone, Debug)] -pub struct FunctionSignatureGen { - return_types: Vec, - arg_types: Vec, - type_formals: Vec, +pub struct SignatureGen { + signatures: Vec, } -impl FunctionSignatureGen { - pub fn strategy( - return_count: impl Into, - arg_count: impl Into, - _kind_count: impl Into, - ) -> impl Strategy { - ( - vec(SignatureTokenGen::strategy(), return_count), - vec(SignatureTokenGen::strategy(), arg_count), - // TODO: re-enable type formals once we rework prop tests for generics - vec(KindGen::strategy(), 0), - ) - .prop_map(|(return_types, arg_types, type_formals)| Self { - return_types, - arg_types, - type_formals, - }) +impl SignatureGen { + pub fn strategy(sig_count: impl Into) -> impl Strategy { + vec(SignatureTokenGen::strategy(), sig_count).prop_map(|signatures| Self { signatures }) } - pub fn materialize(self, struct_handles_len: usize) -> FunctionSignature { - FunctionSignature { - return_types: SignatureTokenGen::map_materialize(self.return_types, struct_handles_len) - .collect(), - arg_types: SignatureTokenGen::map_materialize(self.arg_types, struct_handles_len) - .collect(), - type_formals: self - .type_formals - .into_iter() - .map(KindGen::materialize) - .collect(), - } + pub fn materialize(self, struct_handles_len: usize) -> Signature { + Signature(SignatureTokenGen::map_materialize(self.signatures, struct_handles_len).collect()) } } @@ -85,7 +59,7 @@ pub enum SignatureTokenGen { TypeParameter(PropIndex), // Composite signature tokens. - Struct(PropIndex, Vec), + Struct(PropIndex), Reference(Box), MutableReference(Box), } @@ -132,10 +106,7 @@ impl SignatureTokenGen { } pub fn struct_strategy() -> impl Strategy { - use SignatureTokenGen::*; - - // TODO: generate type actuals - any::().prop_map(|idx| Struct(idx, vec![])) + any::().prop_map(SignatureTokenGen::Struct) } pub fn reference_strategy() -> impl Strategy { @@ -150,20 +121,23 @@ impl SignatureTokenGen { pub fn materialize(self, struct_handles_len: usize) -> SignatureToken { use SignatureTokenGen::*; - match self { Bool => SignatureToken::Bool, U8 => SignatureToken::U8, U64 => SignatureToken::U64, U128 => SignatureToken::U128, Address => SignatureToken::Address, - Struct(idx, types) => SignatureToken::Struct( - StructHandleIndex::new(idx.index(struct_handles_len) as TableIndex), - types - .into_iter() - .map(|t: SignatureTokenGen| t.materialize(struct_handles_len)) - .collect(), - ), + Struct(idx) => { + if struct_handles_len == 0 { + // we are asked to create a type of a struct that cannot exist + // so we fake a U64 instead... + SignatureToken::U64 + } else { + SignatureToken::Struct(StructHandleIndex( + idx.index(struct_handles_len) as TableIndex + )) + } + } Reference(token) => { SignatureToken::Reference(Box::new(token.materialize(struct_handles_len))) } diff --git a/language/vm/src/serializer.rs b/language/vm/src/serializer.rs index 1902f92c68e6..6f5ba3593468 100644 --- a/language/vm/src/serializer.rs +++ b/language/vm/src/serializer.rs @@ -77,9 +77,8 @@ struct CommonSerializer { module_handles: (u32, u32), struct_handles: (u32, u32), function_handles: (u32, u32), - type_signatures: (u32, u32), - function_signatures: (u32, u32), - locals_signatures: (u32, u32), + function_instantiations: (u32, u32), + signatures: (u32, u32), identifiers: (u32, u32), address_pool: (u32, u32), byte_array_pool: (u32, u32), @@ -90,8 +89,10 @@ struct CommonSerializer { struct ModuleSerializer { common: CommonSerializer, struct_defs: (u32, u32), - field_defs: (u32, u32), + struct_def_instantiations: (u32, u32), function_defs: (u32, u32), + field_handles: (u32, u32), + field_instantiations: (u32, u32), } /// Holds data to compute the header of a transaction script binary. @@ -161,12 +162,11 @@ trait CommonTables { fn get_module_handles(&self) -> &[ModuleHandle]; fn get_struct_handles(&self) -> &[StructHandle]; fn get_function_handles(&self) -> &[FunctionHandle]; + fn get_function_instantiations(&self) -> &[FunctionInstantiation]; fn get_identifiers(&self) -> &[Identifier]; fn get_address_pool(&self) -> &[AccountAddress]; fn get_byte_array_pool(&self) -> &[Vec]; - fn get_type_signatures(&self) -> &[TypeSignature]; - fn get_function_signatures(&self) -> &[FunctionSignature]; - fn get_locals_signatures(&self) -> &[LocalsSignature]; + fn get_signatures(&self) -> &[Signature]; } impl CommonTables for CompiledScriptMut { @@ -182,6 +182,10 @@ impl CommonTables for CompiledScriptMut { &self.function_handles } + fn get_function_instantiations(&self) -> &[FunctionInstantiation] { + &self.function_instantiations + } + fn get_identifiers(&self) -> &[Identifier] { &self.identifiers } @@ -194,16 +198,8 @@ impl CommonTables for CompiledScriptMut { &self.byte_array_pool } - fn get_type_signatures(&self) -> &[TypeSignature] { - &self.type_signatures - } - - fn get_function_signatures(&self) -> &[FunctionSignature] { - &self.function_signatures - } - - fn get_locals_signatures(&self) -> &[LocalsSignature] { - &self.locals_signatures + fn get_signatures(&self) -> &[Signature] { + &self.signatures } } @@ -220,6 +216,10 @@ impl CommonTables for CompiledModuleMut { &self.function_handles } + fn get_function_instantiations(&self) -> &[FunctionInstantiation] { + &self.function_instantiations + } + fn get_identifiers(&self) -> &[Identifier] { &self.identifiers } @@ -232,16 +232,8 @@ impl CommonTables for CompiledModuleMut { &self.byte_array_pool } - fn get_type_signatures(&self) -> &[TypeSignature] { - &self.type_signatures - } - - fn get_function_signatures(&self) -> &[FunctionSignature] { - &self.function_signatures - } - - fn get_locals_signatures(&self) -> &[LocalsSignature] { - &self.locals_signatures + fn get_signatures(&self) -> &[Signature] { + &self.signatures } } @@ -266,7 +258,7 @@ fn serialize_struct_handle(binary: &mut BinaryData, struct_handle: &StructHandle write_u16_as_uleb128(binary, struct_handle.module.0)?; write_u16_as_uleb128(binary, struct_handle.name.0)?; serialize_nominal_resource_flag(binary, struct_handle.is_nominal_resource)?; - serialize_kinds(binary, &struct_handle.type_formals) + serialize_kinds(binary, &struct_handle.type_parameters) } /// Serializes a `FunctionHandle`. @@ -274,15 +266,26 @@ fn serialize_struct_handle(binary: &mut BinaryData, struct_handle: &StructHandle /// A `FunctionHandle` gets serialized as follows: /// - `FunctionHandle.module` as a ULEB128 (index into the `ModuleHandle` table) /// - `FunctionHandle.name` as a ULEB128 (index into the `IdentifierPool`) -/// - `FunctionHandle.signature` as a ULEB128 (index into the `FunctionSignaturePool`) +/// - `FunctionHandle.parameters` as a ULEB128 (index into the `SignaturePool`) +/// - `FunctionHandle.return_` as a ULEB128 (index into the `SignaturePool`) +/// - `FunctionHandle.type_parameters` as a `Vec` fn serialize_function_handle( binary: &mut BinaryData, function_handle: &FunctionHandle, ) -> Result<()> { write_u16_as_uleb128(binary, function_handle.module.0)?; write_u16_as_uleb128(binary, function_handle.name.0)?; - write_u16_as_uleb128(binary, function_handle.signature.0)?; - Ok(()) + write_u16_as_uleb128(binary, function_handle.parameters.0)?; + write_u16_as_uleb128(binary, function_handle.return_.0)?; + serialize_kinds(binary, &function_handle.type_parameters) +} + +fn serialize_function_instantiation( + binary: &mut BinaryData, + func_inst: &FunctionInstantiation, +) -> Result<()> { + write_u16_as_uleb128(binary, func_inst.handle.0)?; + write_u16_as_uleb128(binary, func_inst.type_parameters.0) } /// Serializes a string (identifier or user string). @@ -347,20 +350,28 @@ fn serialize_struct_definition( ) -> Result<()> { write_u16_as_uleb128(binary, struct_definition.struct_handle.0)?; match &struct_definition.field_information { - StructFieldInformation::Native => { - binary.push(SerializedNativeStructFlag::NATIVE as u8)?; - write_u16_as_uleb128(binary, 0)?; - write_u16_as_uleb128(binary, 0)?; - } - StructFieldInformation::Declared { - field_count, - fields, - } => { + StructFieldInformation::Native => binary.push(SerializedNativeStructFlag::NATIVE as u8), + StructFieldInformation::Declared(fields) => { binary.push(SerializedNativeStructFlag::DECLARED as u8)?; - write_u16_as_uleb128(binary, *field_count)?; - write_u16_as_uleb128(binary, fields.0)?; + serialize_field_definitions(binary, fields) } - }; + } +} + +fn serialize_struct_def_instantiation( + binary: &mut BinaryData, + struct_inst: &StructDefInstantiation, +) -> Result<()> { + write_u16_as_uleb128(binary, struct_inst.def.0)?; + write_u16_as_uleb128(binary, struct_inst.type_parameters.0) +} + +/// Serializes `FieldDefinition` within a struct. +fn serialize_field_definitions(binary: &mut BinaryData, fields: &[FieldDefinition]) -> Result<()> { + write_u32_as_uleb128(binary, fields.len() as u32)?; + for field_definition in fields { + serialize_field_definition(binary, field_definition)?; + } Ok(()) } @@ -369,15 +380,13 @@ fn serialize_struct_definition( /// A `FieldDefinition` gets serialized as follows: /// - `FieldDefinition.struct_` as a ULEB128 (index into the `StructHandle` table) /// - `StructDefinition.name` as a ULEB128 (index into the `IdentifierPool` table) -/// - `StructDefinition.signature` as a ULEB128 (index into the `TypeSignaturePool`) +/// - `StructDefinition.signature` a serialized `TypeSignatureToekn`) fn serialize_field_definition( binary: &mut BinaryData, field_definition: &FieldDefinition, ) -> Result<()> { - write_u16_as_uleb128(binary, field_definition.struct_.0)?; write_u16_as_uleb128(binary, field_definition.name.0)?; - write_u16_as_uleb128(binary, field_definition.signature.0)?; - Ok(()) + serialize_signature_token(binary, &field_definition.signature.0) } /// Serializes a `FunctionDefinition`. @@ -392,15 +401,25 @@ fn serialize_function_definition( ) -> Result<()> { write_u16_as_uleb128(binary, function_definition.function.0)?; binary.push(function_definition.flags)?; - serialize_struct_definition_indices(binary, &function_definition.acquires_global_resources)?; + serialize_acquires(binary, &function_definition.acquires_global_resources)?; serialize_code_unit(binary, &function_definition.code) } -/// Serializes a `Vec`. -fn serialize_struct_definition_indices( +fn serialize_field_handle(binary: &mut BinaryData, field_handle: &FieldHandle) -> Result<()> { + write_u16_as_uleb128(binary, field_handle.owner.0)?; + write_u16_as_uleb128(binary, field_handle.field) +} + +fn serialize_field_instantiation( binary: &mut BinaryData, - indices: &[StructDefinitionIndex], + field_inst: &FieldInstantiation, ) -> Result<()> { + write_u16_as_uleb128(binary, field_inst.handle.0)?; + write_u16_as_uleb128(binary, field_inst.type_parameters.0) +} + +/// Serializes a `Vec`. +fn serialize_acquires(binary: &mut BinaryData, indices: &[StructDefinitionIndex]) -> Result<()> { let len = indices.len(); if len > u8::max_value() as usize { bail!( @@ -416,39 +435,10 @@ fn serialize_struct_definition_indices( Ok(()) } -/// Serializes a `TypeSignature`. +/// Serializes a `Signature`. /// -/// A `TypeSignature` gets serialized as follows: -/// - `SignatureType::TYPE_SIGNATURE` as 1 byte -/// - The `SignatureToken` as a blob -fn serialize_type_signature(binary: &mut BinaryData, signature: &TypeSignature) -> Result<()> { - binary.push(SignatureType::TYPE_SIGNATURE as u8)?; - serialize_signature_token(binary, &signature.0) -} - -/// Serializes a `FunctionSignature`. -/// -/// A `FunctionSignature` gets serialized as follows: -/// - `SignatureType::FUNCTION_SIGNATURE` as 1 byte -/// - The vector of `SignatureToken`s for the return values -/// - The vector of `SignatureToken`s for the arguments -fn serialize_function_signature( - binary: &mut BinaryData, - signature: &FunctionSignature, -) -> Result<()> { - binary.push(SignatureType::FUNCTION_SIGNATURE as u8)?; - serialize_signature_tokens(binary, &signature.return_types)?; - serialize_signature_tokens(binary, &signature.arg_types)?; - serialize_kinds(binary, &signature.type_formals) -} - -/// Serializes a `LocalsSignature`. -/// -/// A `LocalsSignature` gets serialized as follows: -/// - `SignatureType::LOCAL_SIGNATURE` as 1 byte -/// - The vector of `SignatureToken`s for locals -fn serialize_locals_signature(binary: &mut BinaryData, signature: &LocalsSignature) -> Result<()> { - binary.push(SignatureType::LOCAL_SIGNATURE as u8)?; +/// A `Signature` gets serialized as follows the vector of `SignatureToken`s for locals +fn serialize_signature(binary: &mut BinaryData, signature: &Signature) -> Result<()> { serialize_signature_tokens(binary, &signature.0) } @@ -475,34 +465,37 @@ fn serialize_signature_tokens(binary: &mut BinaryData, tokens: &[SignatureToken] /// Values for types are defined in `SerializedType`. fn serialize_signature_token(binary: &mut BinaryData, token: &SignatureToken) -> Result<()> { match token { - SignatureToken::Bool => binary.push(SerializedType::BOOL as u8)?, - SignatureToken::U8 => binary.push(SerializedType::U8 as u8)?, - SignatureToken::U64 => binary.push(SerializedType::U64 as u8)?, - SignatureToken::U128 => binary.push(SerializedType::U128 as u8)?, - SignatureToken::Address => binary.push(SerializedType::ADDRESS as u8)?, + SignatureToken::Bool => binary.push(SerializedType::BOOL as u8), + SignatureToken::U8 => binary.push(SerializedType::U8 as u8), + SignatureToken::U64 => binary.push(SerializedType::U64 as u8), + SignatureToken::U128 => binary.push(SerializedType::U128 as u8), + SignatureToken::Address => binary.push(SerializedType::ADDRESS as u8), SignatureToken::Vector(boxed_token) => { binary.push(SerializedType::VECTOR as u8)?; - serialize_signature_token(binary, boxed_token)?; + serialize_signature_token(binary, boxed_token) } - SignatureToken::Struct(idx, types) => { + SignatureToken::Struct(idx) => { binary.push(SerializedType::STRUCT as u8)?; + write_u16_as_uleb128(binary, idx.0) + } + SignatureToken::StructInstantiation(idx, type_params) => { + binary.push(SerializedType::STRUCT_INST as u8)?; write_u16_as_uleb128(binary, idx.0)?; - serialize_signature_tokens(binary, types)?; + serialize_signature_tokens(binary, &type_params) } SignatureToken::Reference(boxed_token) => { binary.push(SerializedType::REFERENCE as u8)?; - serialize_signature_token(binary, boxed_token)?; + serialize_signature_token(binary, boxed_token) } SignatureToken::MutableReference(boxed_token) => { binary.push(SerializedType::MUTABLE_REFERENCE as u8)?; - serialize_signature_token(binary, boxed_token)?; + serialize_signature_token(binary, boxed_token) } SignatureToken::TypeParameter(idx) => { binary.push(SerializedType::TYPE_PARAMETER as u8)?; - write_u16_as_uleb128(binary, *idx)?; + write_u16_as_uleb128(binary, *idx) } } - Ok(()) } fn serialize_nominal_resource_flag( @@ -521,7 +514,7 @@ fn serialize_kind(binary: &mut BinaryData, kind: Kind) -> Result<()> { binary.push(match kind { Kind::All => SerializedKind::ALL, Kind::Resource => SerializedKind::RESOURCE, - Kind::Unrestricted => SerializedKind::UNRESTRICTED, + Kind::Copyable => SerializedKind::COPYABLE, } as u8)?; Ok(()) } @@ -614,24 +607,41 @@ fn serialize_instruction_inner(binary: &mut BinaryData, opcode: &Bytecode) -> Re binary.push(Opcodes::MUT_BORROW_FIELD as u8)?; write_u16_as_uleb128(binary, field_idx.0) } + Bytecode::MutBorrowFieldGeneric(field_idx) => { + binary.push(Opcodes::MUT_BORROW_FIELD_GENERIC as u8)?; + write_u16_as_uleb128(binary, field_idx.0) + } Bytecode::ImmBorrowField(field_idx) => { binary.push(Opcodes::IMM_BORROW_FIELD as u8)?; write_u16_as_uleb128(binary, field_idx.0) } - Bytecode::Call(method_idx, types_idx) => { + Bytecode::ImmBorrowFieldGeneric(field_idx) => { + binary.push(Opcodes::IMM_BORROW_FIELD_GENERIC as u8)?; + write_u16_as_uleb128(binary, field_idx.0) + } + Bytecode::Call(method_idx) => { binary.push(Opcodes::CALL as u8)?; - write_u16_as_uleb128(binary, method_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, method_idx.0) } - Bytecode::Pack(class_idx, types_idx) => { + Bytecode::Pack(class_idx) => { binary.push(Opcodes::PACK as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) } - Bytecode::Unpack(class_idx, types_idx) => { + Bytecode::Unpack(class_idx) => { binary.push(Opcodes::UNPACK as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::CallGeneric(method_idx) => { + binary.push(Opcodes::CALL_GENERIC as u8)?; + write_u16_as_uleb128(binary, method_idx.0) + } + Bytecode::PackGeneric(class_idx) => { + binary.push(Opcodes::PACK_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::UnpackGeneric(class_idx) => { + binary.push(Opcodes::UNPACK_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) } Bytecode::ReadRef => binary.push(Opcodes::READ_REF as u8), Bytecode::WriteRef => binary.push(Opcodes::WRITE_REF as u8), @@ -659,30 +669,45 @@ fn serialize_instruction_inner(binary: &mut BinaryData, opcode: &Bytecode) -> Re Bytecode::GetTxnMaxGasUnits => binary.push(Opcodes::GET_TXN_MAX_GAS_UNITS as u8), Bytecode::GetGasRemaining => binary.push(Opcodes::GET_GAS_REMAINING as u8), Bytecode::GetTxnSenderAddress => binary.push(Opcodes::GET_TXN_SENDER as u8), - Bytecode::Exists(class_idx, types_idx) => { + Bytecode::Exists(class_idx) => { binary.push(Opcodes::EXISTS as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) } - Bytecode::MutBorrowGlobal(class_idx, types_idx) => { + Bytecode::MutBorrowGlobal(class_idx) => { binary.push(Opcodes::MUT_BORROW_GLOBAL as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) } - Bytecode::ImmBorrowGlobal(class_idx, types_idx) => { + Bytecode::ImmBorrowGlobal(class_idx) => { binary.push(Opcodes::IMM_BORROW_GLOBAL as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) } - Bytecode::MoveFrom(class_idx, types_idx) => { + Bytecode::MoveFrom(class_idx) => { binary.push(Opcodes::MOVE_FROM as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) } - Bytecode::MoveToSender(class_idx, types_idx) => { + Bytecode::MoveToSender(class_idx) => { binary.push(Opcodes::MOVE_TO as u8)?; - write_u16_as_uleb128(binary, class_idx.0)?; - write_u16_as_uleb128(binary, types_idx.0) + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::ExistsGeneric(class_idx) => { + binary.push(Opcodes::EXISTS_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::MutBorrowGlobalGeneric(class_idx) => { + binary.push(Opcodes::MUT_BORROW_GLOBAL_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::ImmBorrowGlobalGeneric(class_idx) => { + binary.push(Opcodes::IMM_BORROW_GLOBAL_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::MoveFromGeneric(class_idx) => { + binary.push(Opcodes::MOVE_FROM_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) + } + Bytecode::MoveToSenderGeneric(class_idx) => { + binary.push(Opcodes::MOVE_TO_GENERIC as u8)?; + write_u16_as_uleb128(binary, class_idx.0) } Bytecode::GetTxnSequenceNumber => binary.push(Opcodes::GET_TXN_SEQUENCE_NUMBER as u8), Bytecode::GetTxnPublicKey => binary.push(Opcodes::GET_TXN_PUBLIC_KEY as u8), @@ -724,9 +749,8 @@ impl CommonSerializer { module_handles: (0, 0), struct_handles: (0, 0), function_handles: (0, 0), - type_signatures: (0, 0), - function_signatures: (0, 0), - locals_signatures: (0, 0), + function_instantiations: (0, 0), + signatures: (0, 0), identifiers: (0, 0), address_pool: (0, 0), byte_array_pool: (0, 0), @@ -787,24 +811,17 @@ impl CommonSerializer { )?; checked_serialize_table( binary, - TableType::TYPE_SIGNATURES, - self.type_signatures.0, + TableType::FUNCTION_INST, + self.function_instantiations.0, start_offset, - self.type_signatures.1, + self.function_instantiations.1, )?; checked_serialize_table( binary, - TableType::FUNCTION_SIGNATURES, - self.function_signatures.0, + TableType::SIGNATURES, + self.signatures.0, start_offset, - self.function_signatures.1, - )?; - checked_serialize_table( - binary, - TableType::LOCALS_SIGNATURES, - self.locals_signatures.0, - start_offset, - self.locals_signatures.1, + self.signatures.1, )?; checked_serialize_table( binary, @@ -839,10 +856,9 @@ impl CommonSerializer { self.serialize_module_handles(binary, tables.get_module_handles())?; self.serialize_struct_handles(binary, tables.get_struct_handles())?; self.serialize_function_handles(binary, tables.get_function_handles())?; - self.serialize_type_signatures(binary, tables.get_type_signatures())?; - self.serialize_function_signatures(binary, tables.get_function_signatures())?; verify!(self.table_count < 6); // Should not be necessary, but it helps MIRAI right now - self.serialize_locals_signatures(binary, tables.get_locals_signatures())?; + self.serialize_function_instantiations(binary, tables.get_function_instantiations())?; + self.serialize_signatures(binary, tables.get_signatures())?; self.serialize_identifiers(binary, tables.get_identifiers())?; self.serialize_addresses(binary, tables.get_address_pool())?; self.serialize_byte_arrays(binary, tables.get_byte_array_pool())?; @@ -901,6 +917,24 @@ impl CommonSerializer { Ok(()) } + /// Serializes `FunctionInstantiation` table. + fn serialize_function_instantiations( + &mut self, + binary: &mut BinaryData, + function_instantiations: &[FunctionInstantiation], + ) -> Result<()> { + if !function_instantiations.is_empty() { + self.table_count += 1; + self.function_instantiations.0 = check_index_in_binary(binary.len())?; + for function_instantiation in function_instantiations { + serialize_function_instantiation(binary, function_instantiation)?; + } + self.function_instantiations.1 = + checked_calculate_table_size(binary, self.function_instantiations.0)?; + } + Ok(()) + } + /// Serializes `Identifiers`. fn serialize_identifiers( &mut self, @@ -953,55 +987,19 @@ impl CommonSerializer { Ok(()) } - /// Serializes `TypeSignaturePool` table. - fn serialize_type_signatures( - &mut self, - binary: &mut BinaryData, - signatures: &[TypeSignature], - ) -> Result<()> { - if !signatures.is_empty() { - self.table_count += 1; - self.type_signatures.0 = check_index_in_binary(binary.len())?; - for signature in signatures { - serialize_type_signature(binary, signature)?; - } - self.type_signatures.1 = checked_calculate_table_size(binary, self.type_signatures.0)?; - } - Ok(()) - } - - /// Serializes `FunctionSignaturePool` table. - fn serialize_function_signatures( - &mut self, - binary: &mut BinaryData, - signatures: &[FunctionSignature], - ) -> Result<()> { - if !signatures.is_empty() { - self.table_count += 1; - self.function_signatures.0 = check_index_in_binary(binary.len())?; - for signature in signatures { - serialize_function_signature(binary, signature)?; - } - self.function_signatures.1 = - checked_calculate_table_size(binary, self.function_signatures.0)?; - } - Ok(()) - } - - /// Serializes `LocalSignaturePool` table. - fn serialize_locals_signatures( + /// Serializes `SignaturePool` table. + fn serialize_signatures( &mut self, binary: &mut BinaryData, - signatures: &[LocalsSignature], + signatures: &[Signature], ) -> Result<()> { if !signatures.is_empty() { self.table_count += 1; - self.locals_signatures.0 = check_index_in_binary(binary.len())?; + self.signatures.0 = check_index_in_binary(binary.len())?; for signature in signatures { - serialize_locals_signature(binary, signature)?; + serialize_signature(binary, signature)?; } - self.locals_signatures.1 = - checked_calculate_table_size(binary, self.locals_signatures.0)?; + self.signatures.1 = checked_calculate_table_size(binary, self.signatures.0)?; } Ok(()) } @@ -1012,16 +1010,20 @@ impl ModuleSerializer { ModuleSerializer { common: CommonSerializer::new(major_version, minor_version), struct_defs: (0, 0), - field_defs: (0, 0), + struct_def_instantiations: (0, 0), function_defs: (0, 0), + field_handles: (0, 0), + field_instantiations: (0, 0), } } fn serialize(&mut self, binary: &mut BinaryData, module: &CompiledModuleMut) -> Result<()> { self.common.serialize_common(binary, module)?; self.serialize_struct_definitions(binary, &module.struct_defs)?; - self.serialize_field_definitions(binary, &module.field_defs)?; - self.serialize_function_definitions(binary, &module.function_defs) + self.serialize_struct_def_instantiations(binary, &module.struct_def_instantiations)?; + self.serialize_function_definitions(binary, &module.function_defs)?; + self.serialize_field_handles(binary, &module.field_handles)?; + self.serialize_field_instantiations(binary, &module.field_instantiations) } fn serialize_header(&mut self, binary: &mut BinaryData) -> Result<()> { @@ -1035,10 +1037,10 @@ impl ModuleSerializer { )?; checked_serialize_table( binary, - TableType::FIELD_DEFS, - self.field_defs.0, + TableType::STRUCT_DEF_INST, + self.struct_def_instantiations.0, start_offset, - self.field_defs.1, + self.struct_def_instantiations.1, )?; checked_serialize_table( binary, @@ -1047,6 +1049,20 @@ impl ModuleSerializer { start_offset, self.function_defs.1, )?; + checked_serialize_table( + binary, + TableType::FIELD_HANDLE, + self.field_handles.0, + start_offset, + self.field_handles.1, + )?; + checked_serialize_table( + binary, + TableType::FIELD_INST, + self.field_instantiations.0, + start_offset, + self.field_instantiations.1, + )?; Ok(()) } @@ -1067,24 +1083,58 @@ impl ModuleSerializer { Ok(()) } - /// Serializes `FieldDefinition` table. - fn serialize_field_definitions( + /// Serializes `StructInstantiation` table. + fn serialize_struct_def_instantiations( &mut self, binary: &mut BinaryData, - field_definitions: &[FieldDefinition], + struct_def_instantiations: &[StructDefInstantiation], ) -> Result<()> { - if !field_definitions.is_empty() { + if !struct_def_instantiations.is_empty() { self.common.table_count += 1; - self.field_defs.0 = check_index_in_binary(binary.len())?; - for field_definition in field_definitions { - serialize_field_definition(binary, field_definition)?; + self.struct_def_instantiations.0 = check_index_in_binary(binary.len())?; + for struct_instantiation in struct_def_instantiations { + serialize_struct_def_instantiation(binary, struct_instantiation)?; } - self.field_defs.1 = checked_calculate_table_size(binary, self.field_defs.0)?; + self.struct_def_instantiations.1 = + checked_calculate_table_size(binary, self.struct_def_instantiations.0)?; } Ok(()) } /// Serializes `FunctionDefinition` table. + fn serialize_field_handles( + &mut self, + binary: &mut BinaryData, + field_handles: &[FieldHandle], + ) -> Result<()> { + if !field_handles.is_empty() { + self.common.table_count += 1; + self.field_handles.0 = check_index_in_binary(binary.len())?; + for field_handle in field_handles { + serialize_field_handle(binary, field_handle)?; + } + self.field_handles.1 = checked_calculate_table_size(binary, self.field_handles.0)?; + } + Ok(()) + } + + fn serialize_field_instantiations( + &mut self, + binary: &mut BinaryData, + field_instantiations: &[FieldInstantiation], + ) -> Result<()> { + if !field_instantiations.is_empty() { + self.common.table_count += 1; + self.field_instantiations.0 = check_index_in_binary(binary.len())?; + for field_instantiation in field_instantiations { + serialize_field_instantiation(binary, field_instantiation)?; + } + self.field_instantiations.1 = + checked_calculate_table_size(binary, self.field_instantiations.0)?; + } + Ok(()) + } + fn serialize_function_definitions( &mut self, binary: &mut BinaryData, diff --git a/language/vm/src/unit_tests/deserializer_tests.rs b/language/vm/src/unit_tests/deserializer_tests.rs index f3e3e41893ee..a699581bc8df 100644 --- a/language/vm/src/unit_tests/deserializer_tests.rs +++ b/language/vm/src/unit_tests/deserializer_tests.rs @@ -14,7 +14,7 @@ fn malformed_simple() { let mut res = CompiledScript::deserialize(&binary); assert_eq!( res.expect_err("Expected malformed binary").major_status, - StatusCode::MALFORMED + StatusCode::BAD_MAGIC ); // under-sized binary @@ -22,7 +22,7 @@ fn malformed_simple() { res = CompiledScript::deserialize(&binary); assert_eq!( res.expect_err("Expected malformed binary").major_status, - StatusCode::MALFORMED + StatusCode::BAD_MAGIC ); // bad magic diff --git a/language/vm/src/views.rs b/language/vm/src/views.rs index 514edf0b4796..9747e7b0769f 100644 --- a/language/vm/src/views.rs +++ b/language/vm/src/views.rs @@ -13,19 +13,10 @@ use std::iter::DoubleEndedIterator; -use crate::{ - access::ModuleAccess, - file_format::{ - CodeUnit, CompiledModule, FieldDefinition, FunctionDefinition, FunctionHandle, - FunctionSignature, Kind, LocalIndex, LocalsSignature, ModuleHandle, SignatureToken, - StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandle, - StructHandleIndex, TypeSignature, - }, - SignatureTokenKind, -}; +use crate::{access::ModuleAccess, file_format::*, SignatureTokenKind}; use std::collections::BTreeSet; -use libra_types::language_storage::{ModuleId, StructTag}; +use libra_types::language_storage::ModuleId; use move_core_types::identifier::IdentStr; use std::collections::BTreeMap; @@ -87,60 +78,68 @@ impl<'a, T: ModuleAccess> ModuleView<'a, T> { .map(move |function_handle| FunctionHandleView::new(module, function_handle)) } - pub fn structs(&self) -> impl DoubleEndedIterator> + Send { + pub fn field_handles(&self) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .struct_defs() + .field_handles() .iter() - .map(move |struct_def| StructDefinitionView::new(module, struct_def)) + .map(move |field_handle| FieldHandleView::new(module, field_handle)) } - pub fn fields(&self) -> impl DoubleEndedIterator> + Send { + pub fn struct_instantiations( + &self, + ) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .field_defs() + .struct_instantiations() .iter() - .map(move |field_def| FieldDefinitionView::new(module, field_def)) + .map(move |struct_inst| StructInstantiationView::new(module, struct_inst)) } - pub fn functions( + pub fn function_instantiations( &self, - ) -> impl DoubleEndedIterator> + Send { + ) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .function_defs() + .function_instantiations() .iter() - .map(move |function_def| FunctionDefinitionView::new(module, function_def)) + .map(move |func_inst| FunctionInstantiationView::new(module, func_inst)) } - pub fn type_signatures( + pub fn field_instantiations( &self, - ) -> impl DoubleEndedIterator> + Send { + ) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .type_signatures() + .field_instantiations() .iter() - .map(move |type_signature| TypeSignatureView::new(module, type_signature)) + .map(move |field_inst| FieldInstantiationView::new(module, field_inst)) } - pub fn function_signatures( - &self, - ) -> impl DoubleEndedIterator> + Send { + pub fn signatures(&self) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .function_signatures() + .signatures() .iter() - .map(move |function_signature| FunctionSignatureView::new(module, function_signature)) + .map(move |signature| SignatureView::new(module, signature)) } - pub fn locals_signatures( + pub fn structs(&self) -> impl DoubleEndedIterator> + Send { + let module = self.module; + module + .struct_defs() + .iter() + .map(move |struct_def| StructDefinitionView::new(module, struct_def)) + } + + pub fn functions( &self, - ) -> impl DoubleEndedIterator> + Send { + ) -> impl DoubleEndedIterator> + Send { let module = self.module; module - .locals_signatures() + .function_defs() .iter() - .map(move |locals_signature| LocalsSignatureView::new(module, locals_signature)) + .map(move |function_def| FunctionDefinitionView::new(module, function_def)) } pub fn function_definition( @@ -176,17 +175,6 @@ impl<'a, T: ModuleAccess> ModuleView<'a, T> { pub fn id(&self) -> ModuleId { self.module.self_id() } - - /// Return the `StructHandleIndex` that corresponds to the normalized type `t` in this module's - /// table of `StructHandle`'s. Returns `None` if there is no corresponding handle - pub fn resolve_struct(&self, t: &StructTag) -> Option { - for (idx, handle) in self.module.struct_handles().iter().enumerate() { - if &StructHandleView::new(self.module, handle).normalize_struct() == t { - return Some(StructHandleIndex::new(idx as u16)); - } - } - None - } } pub struct ModuleHandleView<'a, T> { @@ -228,8 +216,8 @@ impl<'a, T: ModuleAccess> StructHandleView<'a, T> { self.struct_handle.is_nominal_resource } - pub fn type_formals(&self) -> &Vec { - &self.struct_handle.type_formals + pub fn type_parameters(&self) -> &Vec { + &self.struct_handle.type_parameters } pub fn module_handle(&self) -> &ModuleHandle { @@ -253,18 +241,6 @@ impl<'a, T: ModuleAccess> StructHandleView<'a, T> { } unreachable!("Cannot resolve StructHandle {:?} in module {:?}. This should never happen in a well-formed `StructHandleView`. Perhaps this handle came from a different module?", self.handle(), self.module().name()) } - - /// Return a normalized representation of this struct type that can be compared across modules - pub fn normalize_struct(&self) -> StructTag { - let module_id = self.module_id(); - StructTag { - module: module_id.name().into(), - address: *module_id.address(), - name: self.name().into(), - // TODO: take type params as input - type_params: vec![], - } - } } pub struct FunctionHandleView<'a, T> { @@ -288,11 +264,51 @@ impl<'a, T: ModuleAccess> FunctionHandleView<'a, T> { self.module.identifier_at(self.function_handle.name) } - pub fn signature(&self) -> FunctionSignatureView<'a, T> { - let function_signature = self - .module - .function_signature_at(self.function_handle.signature); - FunctionSignatureView::new(self.module, function_signature) + pub fn parameters(&self) -> &'a Signature { + self.module.signature_at(self.function_handle.parameters) + } + + pub fn return_(&self) -> &'a Signature { + self.module.signature_at(self.function_handle.return_) + } + + #[inline] + pub fn return_tokens(&self) -> impl DoubleEndedIterator> + 'a { + let module = self.module; + let return_ = self.module.signature_at(self.function_handle.return_); + return_ + .0 + .iter() + .map(move |token| SignatureTokenView::new(module, token)) + } + + #[inline] + pub fn arg_tokens(&self) -> impl DoubleEndedIterator> + 'a { + let module = self.module; + let parameters = self.module.signature_at(self.function_handle.parameters); + parameters + .0 + .iter() + .map(move |token| SignatureTokenView::new(module, token)) + } + + #[inline] + pub fn type_parameters(&self) -> &Vec { + &self.function_handle.type_parameters + } + + pub fn return_count(&self) -> usize { + self.module + .signature_at(self.function_handle.return_) + .0 + .len() + } + + pub fn arg_count(&self) -> usize { + self.module + .signature_at(self.function_handle.parameters) + .0 + .len() } pub fn module_id(&self) -> ModuleId { @@ -328,22 +344,18 @@ impl<'a, T: ModuleAccess> StructDefinitionView<'a, T> { } } - pub fn type_formals(&self) -> &Vec { - self.struct_handle_view.type_formals() + pub fn type_parameters(&self) -> &Vec { + self.struct_handle_view.type_parameters() } pub fn fields( &self, ) -> Option> + Send> { let module = self.module; - match self.struct_def.field_information { + match &self.struct_def.field_information { StructFieldInformation::Native => None, - StructFieldInformation::Declared { - field_count, - fields, - } => Some( - module - .field_def_range(field_count, fields) + StructFieldInformation::Declared(fields) => Some( + fields .iter() .map(move |field_def| FieldDefinitionView::new(module, field_def)), ), @@ -353,11 +365,6 @@ impl<'a, T: ModuleAccess> StructDefinitionView<'a, T> { pub fn name(&self) -> &'a IdentStr { self.struct_handle_view.name() } - - /// Return a normalized representation of this struct type that can be compared across modules - pub fn normalize_struct(&self) -> StructTag { - self.struct_handle_view.normalize_struct() - } } pub struct FieldDefinitionView<'a, T> { @@ -375,31 +382,16 @@ impl<'a, T: ModuleAccess> FieldDefinitionView<'a, T> { } pub fn type_signature(&self) -> TypeSignatureView<'a, T> { - let type_signature = self.module.type_signature_at(self.field_def.signature); - TypeSignatureView::new(self.module, type_signature) + TypeSignatureView::new(self.module, &self.field_def.signature) } pub fn signature_token(&self) -> &'a SignatureToken { - &self.module.type_signature_at(self.field_def.signature).0 + &self.field_def.signature.0 } pub fn signature_token_view(&self) -> SignatureTokenView<'a, T> { SignatureTokenView::new(self.module, self.signature_token()) } - - // Field definitions are always private. - - /// The struct this field is defined in. - pub fn member_of(&self) -> StructHandleView<'a, T> { - let struct_handle = self.module.struct_handle_at(self.field_def.struct_); - StructHandleView::new(self.module, struct_handle) - } - - /// Return a normalized representation of the type of this field's declaring struct that can be - /// compared across modules - pub fn normalize_declaring_struct(&self) -> StructTag { - self.member_of().normalize_struct() - } } pub struct FunctionDefinitionView<'a, T> { @@ -427,19 +419,41 @@ impl<'a, T: ModuleAccess> FunctionDefinitionView<'a, T> { self.function_def.is_native() } - pub fn locals_signature(&self) -> LocalsSignatureView<'a, T> { - let locals_signature = self - .module - .locals_signature_at(self.function_def.code.locals); - LocalsSignatureView::new(self.module, locals_signature) + pub fn locals_signature(&self) -> SignatureView<'a, T> { + let locals_signature = self.module.signature_at(self.function_def.code.locals); + SignatureView::new(self.module, locals_signature) } pub fn name(&self) -> &'a IdentStr { self.function_handle_view.name() } - pub fn signature(&self) -> FunctionSignatureView<'a, T> { - self.function_handle_view.signature() + pub fn parameters(&self) -> &'a Signature { + self.function_handle_view.parameters() + } + + pub fn return_(&self) -> &'a Signature { + self.function_handle_view.return_() + } + + pub fn type_parameters(&self) -> &Vec { + self.function_handle_view.type_parameters() + } + + pub fn return_tokens(&self) -> impl DoubleEndedIterator> + 'a { + self.function_handle_view.return_tokens() + } + + pub fn arg_tokens(&self) -> impl DoubleEndedIterator> + 'a { + self.function_handle_view.arg_tokens() + } + + pub fn return_count(&self) -> usize { + self.function_handle_view.return_count() + } + + pub fn arg_count(&self) -> usize { + self.function_handle_view.arg_count() } pub fn code(&self) -> &'a CodeUnit { @@ -447,99 +461,102 @@ impl<'a, T: ModuleAccess> FunctionDefinitionView<'a, T> { } } -pub struct TypeSignatureView<'a, T> { +pub struct StructInstantiationView<'a, T> { + #[allow(unused)] module: &'a T, - type_signature: &'a TypeSignature, + #[allow(unused)] + struct_inst: &'a StructDefInstantiation, } -impl<'a, T: ModuleAccess> TypeSignatureView<'a, T> { +impl<'a, T: ModuleAccess> StructInstantiationView<'a, T> { #[inline] - pub fn new(module: &'a T, type_signature: &'a TypeSignature) -> Self { + pub fn new(module: &'a T, struct_inst: &'a StructDefInstantiation) -> Self { Self { module, - type_signature, + struct_inst, } } +} +pub struct FieldHandleView<'a, T> { + #[allow(unused)] + module: &'a T, + #[allow(unused)] + field_handle: &'a FieldHandle, +} + +impl<'a, T: ModuleAccess> FieldHandleView<'a, T> { #[inline] - pub fn token(&self) -> SignatureTokenView<'a, T> { - SignatureTokenView::new(self.module, &self.type_signature.0) + pub fn new(module: &'a T, field_handle: &'a FieldHandle) -> Self { + Self { + module, + field_handle, + } } +} + +pub struct FunctionInstantiationView<'a, T> { + #[allow(unused)] + module: &'a T, + #[allow(unused)] + func_inst: &'a FunctionInstantiation, +} +impl<'a, T: ModuleAccess> FunctionInstantiationView<'a, T> { #[inline] - pub fn kind(&self, type_formals: &[Kind]) -> Kind { - self.token().kind(type_formals) + pub fn new(module: &'a T, func_inst: &'a FunctionInstantiation) -> Self { + Self { module, func_inst } } +} +pub struct FieldInstantiationView<'a, T> { + #[allow(unused)] + module: &'a T, + #[allow(unused)] + field_inst: &'a FieldInstantiation, +} + +impl<'a, T: ModuleAccess> FieldInstantiationView<'a, T> { #[inline] - pub fn contains_nominal_resource(&self, type_formals: &[Kind]) -> bool { - self.token().contains_nominal_resource(type_formals) + pub fn new(module: &'a T, field_inst: &'a FieldInstantiation) -> Self { + Self { module, field_inst } } } -pub struct FunctionSignatureView<'a, T> { +pub struct TypeSignatureView<'a, T> { module: &'a T, - function_signature: &'a FunctionSignature, + type_signature: &'a TypeSignature, } -impl<'a, T: ModuleAccess> FunctionSignatureView<'a, T> { +impl<'a, T: ModuleAccess> TypeSignatureView<'a, T> { #[inline] - pub fn new(module: &'a T, function_signature: &'a FunctionSignature) -> Self { + pub fn new(module: &'a T, type_signature: &'a TypeSignature) -> Self { Self { module, - function_signature, + type_signature, } } #[inline] - pub fn return_tokens(&self) -> impl DoubleEndedIterator> + 'a { - let module = self.module; - self.function_signature - .return_types - .iter() - .map(move |token| SignatureTokenView::new(module, token)) - } - - #[inline] - pub fn arg_tokens(&self) -> impl DoubleEndedIterator> + 'a { - let module = self.module; - self.function_signature - .arg_types - .iter() - .map(move |token| SignatureTokenView::new(module, token)) - } - - #[inline] - pub fn type_formals(&self) -> &Vec { - &self.function_signature.type_formals - } - - pub fn return_count(&self) -> usize { - self.function_signature.return_types.len() - } - - pub fn arg_count(&self) -> usize { - self.function_signature.arg_types.len() + pub fn token(&self) -> SignatureTokenView<'a, T> { + SignatureTokenView::new(self.module, &self.type_signature.0) } } -pub struct LocalsSignatureView<'a, T> { +pub struct SignatureView<'a, T> { module: &'a T, - locals_signature: &'a LocalsSignature, + signature: &'a Signature, } -impl<'a, T: ModuleAccess> LocalsSignatureView<'a, T> { +impl<'a, T: ModuleAccess> SignatureView<'a, T> { #[inline] - pub fn new(module: &'a T, locals_signature: &'a LocalsSignature) -> Self { - Self { - module, - locals_signature, - } + pub fn new(module: &'a T, signature: &'a Signature) -> Self { + Self { module, signature } } #[inline] pub fn len(&self) -> usize { - self.locals_signature.0.len() + self.signature.0.len() } #[inline] @@ -550,14 +567,14 @@ impl<'a, T: ModuleAccess> LocalsSignatureView<'a, T> { #[inline] pub fn tokens(&self) -> impl DoubleEndedIterator> + 'a { let module = self.module; - self.locals_signature + self.signature .0 .iter() .map(move |token| SignatureTokenView::new(module, token)) } pub fn token_at(&self, index: LocalIndex) -> SignatureTokenView<'a, T> { - SignatureTokenView::new(self.module, &self.locals_signature.0[index as usize]) + SignatureTokenView::new(self.module, &self.signature.0[index as usize]) } } @@ -572,12 +589,6 @@ impl<'a, T: ModuleAccess> SignatureTokenView<'a, T> { Self { module, token } } - #[inline] - pub fn struct_handle(&self) -> Option> { - self.struct_index() - .map(|sh_idx| StructHandleView::new(self.module, self.module.struct_handle_at(sh_idx))) - } - #[inline] pub fn signature_token(&self) -> &SignatureToken { self.token @@ -588,44 +599,6 @@ impl<'a, T: ModuleAccess> SignatureTokenView<'a, T> { self.token.signature_token_kind() } - // TODO: rework views to make the interfaces here cleaner. - pub fn kind(&self, type_formals: &[Kind]) -> Kind { - SignatureToken::kind((self.module.struct_handles(), type_formals), self.token) - } - - /// Determines if the given signature token contains a nominal resource. - /// More specifically, a signature token contains a nominal resource if - /// 1) it is a type variable explicitly marked as resource kind. - /// 2) it is a struct that - /// a) is marked as resource. - /// b) has a type actual which is a nominal resource. - /// - /// Similar to `SignatureTokenView::kind`, the context is used for looking up struct - /// definitions & type formals. - // TODO: refactor views so that we get the type formals from self. - pub fn contains_nominal_resource(&self, type_formals: &[Kind]) -> bool { - match self.token { - SignatureToken::Struct(sh_idx, type_arguments) => { - StructHandleView::new(self.module, self.module.struct_handle_at(*sh_idx)) - .is_nominal_resource() - || type_arguments.iter().any(|token| { - Self::new(self.module, token).contains_nominal_resource(type_formals) - }) - } - SignatureToken::Vector(ty) => { - SignatureTokenView::new(self.module, ty).contains_nominal_resource(type_formals) - } - SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) - | SignatureToken::Bool - | SignatureToken::U8 - | SignatureToken::U64 - | SignatureToken::U128 - | SignatureToken::Address - | SignatureToken::TypeParameter(_) => false, - } - } - #[inline] pub fn is_reference(&self) -> bool { self.token.is_reference() @@ -635,44 +608,6 @@ impl<'a, T: ModuleAccess> SignatureTokenView<'a, T> { pub fn is_mutable_reference(&self) -> bool { self.token.is_mutable_reference() } - - #[inline] - pub fn struct_index(&self) -> Option { - self.token.struct_index() - } - - /// If `self` is a struct or reference to a struct, return a normalized representation of this - /// struct type that can be compared across modules - pub fn normalize_struct(&self) -> Option { - self.struct_handle().map(|handle| handle.normalize_struct()) - } - - /// Return the equivalent `SignatureToken` for `self` inside `module` - pub fn resolve_in_module(&self, other_module: &T) -> Option { - if let Some(struct_handle) = self.struct_handle() { - // Token contains a struct handle from `self.module`. Need to resolve inside - // `other_module`. We do this by normalizing the struct in `self.token`, then - // searching for the normalized representation inside `other_module` - // TODO: do we need to resolve `self.token`'s type actuals? - let type_actuals = Vec::new(); - ModuleView::new(other_module) - .resolve_struct(&struct_handle.normalize_struct()) - .map(|handle_idx| SignatureToken::Struct(handle_idx, type_actuals)) - } else { - Some(self.token.clone()) - } - } -} - -impl<'a, T: ModuleAccess> ::std::fmt::Debug for SignatureTokenView<'a, T> { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self.normalize_struct() { - Some(s) if self.is_reference() => write!(f, "&{:?}", s), - Some(s) if self.is_mutable_reference() => write!(f, "&mut {:?}", s), - Some(s) => s.fmt(f), - None => self.token.fmt(f), - } - } } /// This is used to expose some view internals to checks and other areas. This might be exposed @@ -724,6 +659,5 @@ impl_view_internals!(StructDefinitionView, StructDefinition, struct_def); impl_view_internals!(FunctionDefinitionView, FunctionDefinition, function_def); impl_view_internals!(FieldDefinitionView, FieldDefinition, field_def); impl_view_internals!(TypeSignatureView, TypeSignature, type_signature); -impl_view_internals!(FunctionSignatureView, FunctionSignature, function_signature); -impl_view_internals!(LocalsSignatureView, LocalsSignature, locals_signature); +impl_view_internals!(SignatureView, Signature, signature); impl_view_internals!(SignatureTokenView, SignatureToken, token); diff --git a/types/src/vm_error.rs b/types/src/vm_error.rs index 3992b66296b5..2e451e50d773 100644 --- a/types/src/vm_error.rs +++ b/types/src/vm_error.rs @@ -378,7 +378,7 @@ pub enum StatusCode { INVALID_ACQUIRES_RESOURCE_ANNOTATION_ERROR = 1073, GLOBAL_REFERENCE_ERROR = 1074, CONTRAINT_KIND_MISMATCH = 1075, - NUMBER_OF_TYPE_ACTUALS_MISMATCH = 1076, + NUMBER_OF_TYPE_ARGUMENTS_MISMATCH = 1076, LOOP_IN_INSTANTIATION_GRAPH = 1077, UNUSED_LOCALS_SIGNATURE = 1078, UNUSED_TYPE_SIGNATURE = 1079, @@ -418,6 +418,15 @@ pub enum StatusCode { UNEXPECTED_SIGNATURE_TYPE = 3009, DUPLICATE_TABLE = 3010, VERIFIER_INVARIANT_VIOLATION = 3011, + UNKNOWN_NOMINAL_RESOURCE = 3012, + UNKNOWN_KIND = 3013, + UNKNOWN_NATIVE_STRUCT_FLAG = 3014, + BAD_ULEB_U16 = 3015, + BAD_ULEB_U32 = 3016, + BAD_U16 = 3017, + BAD_U32 = 3018, + BAD_U64 = 3019, + BAD_U128 = 3020, // Errors that can arise at runtime // Runtime Errors: 4000-4999 @@ -591,7 +600,7 @@ impl TryFrom for StatusCode { 1073 => Ok(StatusCode::INVALID_ACQUIRES_RESOURCE_ANNOTATION_ERROR), 1074 => Ok(StatusCode::GLOBAL_REFERENCE_ERROR), 1075 => Ok(StatusCode::CONTRAINT_KIND_MISMATCH), - 1076 => Ok(StatusCode::NUMBER_OF_TYPE_ACTUALS_MISMATCH), + 1076 => Ok(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH), 1077 => Ok(StatusCode::LOOP_IN_INSTANTIATION_GRAPH), 1078 => Ok(StatusCode::UNUSED_LOCALS_SIGNATURE), 1079 => Ok(StatusCode::UNUSED_TYPE_SIGNATURE),