diff --git a/Cargo.lock b/Cargo.lock index e7b68df2dfa02..f31dca3a259fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6924,6 +6924,7 @@ dependencies = [ "move-bytecode-source-map", "move-bytecode-verifier", "move-command-line-common", + "move-core-types", "move-ir-to-bytecode", "serde_json", ] diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index 1ad62df6d7640..83f44dd514ad7 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -1867,6 +1867,7 @@ dependencies = [ "move-bytecode-source-map", "move-bytecode-verifier", "move-command-line-common", + "move-core-types", "move-ir-to-bytecode", "serde_json", ] diff --git a/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.exp b/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.exp new file mode 100644 index 0000000000000..00c6572ee8643 --- /dev/null +++ b/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.exp @@ -0,0 +1,41 @@ +processed 3 tasks + +task 2 'print-bytecode'. lines 16-36: +// Move bytecode v6 +module 1.M { +use 0000000000000000000000000000000000000000000000000000000000000001::T; + + + + + + +f(): address { +B0: + 0: Call T::f() + 1: LdConst[0](address: 0x00..) + 2: Ret + +} +h(): address { +B0: + 0: LdConst[0](address: 0x00..) + 1: Call T::g(address): address + 2: Ret + +} +l(): address { +L0: loc0: address +B0: + 0: LdConst[0](address: 0x00..) + 1: StLoc[0](loc0: address) + 2: MoveLoc[0](loc0: address) + 3: Call T::g(address): address + 4: Ret + +} + +Constants [ + 0 => address: 0x0000000000000000000000000000000000000000000000000000000000000001 +] +} diff --git a/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.mvir b/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.mvir new file mode 100644 index 0000000000000..10f8f86b2641e --- /dev/null +++ b/external-crates/move/crates/move-ir-compiler-transactional-tests/tests/parsing/named_addresses.mvir @@ -0,0 +1,36 @@ +//# init --addresses A=0x1 + +//# publish +module A.T { + f() { + label l0: + return; + } + + g(a: address): address { + label l0: + return move(a); + } +} + +//# print-bytecode +module A.M { + import A.T; + f(): address { + label l0: + T.f(); + return @A; + } + + h(): address { + label l0: + return T.g(@A); + } + + l(): address { + let t: address; + label l0: + t = @A; + return T.g(move(t)); + } +} diff --git a/external-crates/move/crates/move-ir-compiler/Cargo.toml b/external-crates/move/crates/move-ir-compiler/Cargo.toml index 01d6206c39b46..dd4a4a5d57baf 100644 --- a/external-crates/move/crates/move-ir-compiler/Cargo.toml +++ b/external-crates/move/crates/move-ir-compiler/Cargo.toml @@ -17,6 +17,7 @@ move-command-line-common.workspace = true move-ir-to-bytecode.workspace = true move-bytecode-source-map.workspace = true move-binary-format.workspace = true +move-core-types.workspace = true clap.workspace = true serde_json.workspace = true diff --git a/external-crates/move/crates/move-ir-compiler/src/lib.rs b/external-crates/move/crates/move-ir-compiler/src/lib.rs index 175731dd53d29..6e2d710d1ff81 100644 --- a/external-crates/move/crates/move-ir-compiler/src/lib.rs +++ b/external-crates/move/crates/move-ir-compiler/src/lib.rs @@ -9,21 +9,36 @@ pub mod util; #[cfg(test)] mod unit_tests; +use std::collections::BTreeMap; + use anyhow::Result; use move_binary_format::file_format::CompiledModule; use move_bytecode_source_map::source_map::SourceMap; -use move_ir_to_bytecode::{compiler::compile_module, parser::parse_module}; +use move_core_types::account_address::AccountAddress; +use move_ir_to_bytecode::{compiler::compile_module, parser::parse_module_with_named_addresses}; /// An API for the compiler. Supports setting custom options. #[derive(Clone, Debug)] pub struct Compiler<'a> { /// Extra dependencies to compile with. pub deps: Vec<&'a CompiledModule>, + pub named_addresses: BTreeMap, } impl<'a> Compiler<'a> { pub fn new(deps: Vec<&'a CompiledModule>) -> Self { - Self { deps } + Self { + deps, + named_addresses: BTreeMap::new(), + } + } + + pub fn with_named_addresses( + mut self, + named_addresses: BTreeMap, + ) -> Self { + self.named_addresses.extend(named_addresses); + self } /// Compiles the module. @@ -41,7 +56,7 @@ impl<'a> Compiler<'a> { } fn compile_mod(self, code: &str) -> Result<(CompiledModule, SourceMap)> { - let parsed_module = parse_module(code)?; + let parsed_module = parse_module_with_named_addresses(code, &self.named_addresses)?; let (compiled_module, source_map) = compile_module(parsed_module, self.deps.iter().copied())?; Ok((compiled_module, source_map)) diff --git a/external-crates/move/crates/move-ir-to-bytecode-syntax/src/lexer.rs b/external-crates/move/crates/move-ir-to-bytecode-syntax/src/lexer.rs index 00fb8cf194c9c..da0c9347547ca 100644 --- a/external-crates/move/crates/move-ir-to-bytecode-syntax/src/lexer.rs +++ b/external-crates/move/crates/move-ir-to-bytecode-syntax/src/lexer.rs @@ -4,7 +4,9 @@ use crate::syntax::ParseError; use move_command_line_common::files::FileHash; +use move_core_types::account_address::AccountAddress; use move_ir_types::location::*; +use std::collections::BTreeMap; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Tok { @@ -90,6 +92,7 @@ pub enum Tok { RSquare, Enum, VariantSwitch, + At, } pub struct Lexer<'input> { @@ -100,10 +103,15 @@ pub struct Lexer<'input> { cur_start: usize, cur_end: usize, token: Tok, + named_addresses: &'input BTreeMap, } impl<'input> Lexer<'input> { - pub fn new(file_hash: FileHash, s: &'input str) -> Lexer<'input> { + pub fn new( + file_hash: FileHash, + s: &'input str, + named_addresses: &'input BTreeMap, + ) -> Lexer<'input> { Lexer { spec_mode: false, // read tokens without trailing punctuation during specs. file_hash, @@ -112,6 +120,7 @@ impl<'input> Lexer<'input> { cur_start: 0, cur_end: 0, token: Tok::EOF, + named_addresses, } } @@ -135,6 +144,10 @@ impl<'input> Lexer<'input> { self.prev_end } + pub fn resolve_named_address(&self, name: &str) -> Option { + self.named_addresses.get(name).cloned() + } + fn trim_whitespace_and_comments(&self) -> &'input str { let mut text = &self.text[self.cur_end..]; loop { @@ -266,6 +279,7 @@ impl<'input> Lexer<'input> { (get_name_token(name), len) // just return the name in spec_mode } } + '@' => (Tok::At, 1), '&' => { if text.starts_with("&mut ") { (Tok::AmpMut, 5) diff --git a/external-crates/move/crates/move-ir-to-bytecode-syntax/src/syntax.rs b/external-crates/move/crates/move-ir-to-bytecode-syntax/src/syntax.rs index 89c4889470354..dd7a22df0f5f4 100644 --- a/external-crates/move/crates/move-ir-to-bytecode-syntax/src/syntax.rs +++ b/external-crates/move/crates/move-ir-to-bytecode-syntax/src/syntax.rs @@ -2,8 +2,12 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, Context}; -use std::{collections::BTreeSet, fmt, str::FromStr}; +use anyhow::anyhow; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt, + str::FromStr, +}; use crate::lexer::*; use move_command_line_common::files::FileHash; @@ -182,24 +186,35 @@ fn parse_dot_name<'input>( fn parse_account_address( tokens: &mut Lexer, ) -> Result> { - if tokens.peek() != Tok::AccountAddressValue { + if !matches!(tokens.peek(), Tok::AccountAddressValue | Tok::NameValue) { return Err(ParseError::InvalidToken { location: current_token_loc(tokens), message: "expected Tok::AccountAddressValue".to_string(), }); } - let addr = AccountAddress::from_hex_literal(tokens.content()) - .with_context(|| { - format!( - "The address {:?} is of invalid length. Addresses are at most 32-bytes long", - tokens.content() - ) - }) - .unwrap(); + let loc = current_token_loc(tokens); + let addr = parse_address_literal(tokens, tokens.content(), loc).unwrap(); tokens.advance()?; Ok(addr) } +fn parse_address_literal( + lexer: &Lexer, + literal: &str, + location: Loc, +) -> Result> { + let Some(addr) = AccountAddress::from_hex_literal(literal) + .ok() + .or_else(|| lexer.resolve_named_address(literal)) + else { + return Err(ParseError::InvalidToken { + location, + message: format!("Invalid address '{}'", literal), + }); + }; + Ok(addr) +} + // Var: Var = { // =>? Var::parse(n), // }; @@ -741,6 +756,11 @@ fn parse_term_(tokens: &mut Lexer) -> Result { + tokens.advance()?; + let address = parse_account_address(tokens)?; + Ok(Exp_::address(address).value) + } t => Err(ParseError::InvalidToken { location: current_token_loc(tokens), message: format!("unrecognized token kind for term {:?}", t), @@ -1789,6 +1809,14 @@ fn parse_variant_decl( // } fn parse_module_ident(tokens: &mut Lexer) -> Result> { + if tokens.peek() == Tok::DotNameValue { + let start_loc = current_token_loc(tokens); + let module_dot_name = parse_dot_name(tokens)?; + let v: Vec<&str> = module_dot_name.split('.').collect(); + assert!(v.len() == 2); + let address = parse_address_literal(tokens, v[0], start_loc)?; + return Ok(ModuleIdent::new(ModuleName(Symbol::from(v[1])), address)); + } let a = parse_account_address(tokens)?; consume_token(tokens, Tok::Period)?; let m = parse_module_name(tokens)?; @@ -1914,9 +1942,16 @@ fn parse_module(tokens: &mut Lexer) -> Result Result> { + parse_module_string_with_named_addresses(input, &BTreeMap::new()) +} + +pub fn parse_module_string_with_named_addresses( + input: &str, + named_addresses: &BTreeMap, ) -> Result> { let file_hash = FileHash::new(input); - let mut tokens = Lexer::new(file_hash, input); + let mut tokens = Lexer::new(file_hash, input, named_addresses); tokens.advance()?; let unit = parse_module(&mut tokens)?; consume_token(&mut tokens, Tok::EOF)?; diff --git a/external-crates/move/crates/move-ir-to-bytecode/src/parser.rs b/external-crates/move/crates/move-ir-to-bytecode/src/parser.rs index 08b2e3b7723f1..26e0068aac6ce 100644 --- a/external-crates/move/crates/move-ir-to-bytecode/src/parser.rs +++ b/external-crates/move/crates/move-ir-to-bytecode/src/parser.rs @@ -13,8 +13,10 @@ use codespan_reporting::{ }, }; use move_command_line_common::character_sets::is_permitted_chars; +use move_core_types::account_address::AccountAddress; use move_ir_to_bytecode_syntax::syntax::{self, ParseError}; use move_ir_types::{ast, location::*}; +use std::collections::BTreeMap; // We restrict strings to only ascii visual characters (0x20 <= c <= 0x7E) or a permitted newline // character--\r--,--\n--or a tab--\t. Checking each character in the input string is more fool-proof @@ -39,8 +41,18 @@ fn verify_string(string: &str) -> Result<()> { /// Given the raw input of a file, creates a single `ModuleDefinition` struct /// Fails with `Err(_)` if the text cannot be parsed pub fn parse_module(modules_str: &str) -> Result { + parse_module_with_named_addresses(modules_str, &BTreeMap::new()) +} + +/// Given the raw input of a file, creates a single `ModuleDefinition` struct +/// Fails with `Err(_)` if the text cannot be parsed +pub fn parse_module_with_named_addresses( + modules_str: &str, + named_address_mapping: &BTreeMap, +) -> Result { verify_string(modules_str)?; - syntax::parse_module_string(modules_str).or_else(|e| handle_error(e, modules_str)) + syntax::parse_module_string_with_named_addresses(modules_str, named_address_mapping) + .or_else(|e| handle_error(e, modules_str)) } fn handle_error(e: syntax::ParseError, code_str: &str) -> Result { diff --git a/external-crates/move/crates/move-transactional-test-runner/src/framework.rs b/external-crates/move/crates/move-transactional-test-runner/src/framework.rs index cde08bf98dc23..93d97b02b2361 100644 --- a/external-crates/move/crates/move-transactional-test-runner/src/framework.rs +++ b/external-crates/move/crates/move-transactional-test-runner/src/framework.rs @@ -672,7 +672,14 @@ pub fn compile_ir_module( ) -> Result { use move_ir_compiler::Compiler as IRCompiler; let code = std::fs::read_to_string(file_name).unwrap(); - IRCompiler::new(state.dep_modules().collect()).into_compiled_module(&code) + let named_addresses = state + .named_address_mapping + .iter() + .map(|(name, addr)| (name.clone(), addr.into_inner())) + .collect(); + IRCompiler::new(state.dep_modules().collect()) + .with_named_addresses(named_addresses) + .into_compiled_module(&code) } pub async fn handle_actual_output<'a, Adapter>(