Skip to content

Commit

Permalink
[move] Add named addresses to Move IR syntax (#18317)
Browse files Browse the repository at this point in the history
## Description 

Little PR that adds support for using named addresses in Move IR source
syntax.

## Test plan 

Added a test to make sure that we handle them correctly, and existing
tests.
  • Loading branch information
tzakian authored Jun 20, 2024
1 parent 4c16fc1 commit b5b0016
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions external-crates/move/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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
]
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
1 change: 1 addition & 0 deletions external-crates/move/crates/move-ir-compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 18 additions & 3 deletions external-crates/move/crates/move-ir-compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, AccountAddress>,
}

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<String, AccountAddress>,
) -> Self {
self.named_addresses.extend(named_addresses);
self
}

/// Compiles the module.
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -90,6 +92,7 @@ pub enum Tok {
RSquare,
Enum,
VariantSwitch,
At,
}

pub struct Lexer<'input> {
Expand All @@ -100,10 +103,15 @@ pub struct Lexer<'input> {
cur_start: usize,
cur_end: usize,
token: Tok,
named_addresses: &'input BTreeMap<String, AccountAddress>,
}

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<String, AccountAddress>,
) -> Lexer<'input> {
Lexer {
spec_mode: false, // read tokens without trailing punctuation during specs.
file_hash,
Expand All @@ -112,6 +120,7 @@ impl<'input> Lexer<'input> {
cur_start: 0,
cur_end: 0,
token: Tok::EOF,
named_addresses,
}
}

Expand All @@ -135,6 +144,10 @@ impl<'input> Lexer<'input> {
self.prev_end
}

pub fn resolve_named_address(&self, name: &str) -> Option<AccountAddress> {
self.named_addresses.get(name).cloned()
}

fn trim_whitespace_and_comments(&self) -> &'input str {
let mut text = &self.text[self.cur_end..];
loop {
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -182,24 +186,35 @@ fn parse_dot_name<'input>(
fn parse_account_address(
tokens: &mut Lexer,
) -> Result<AccountAddress, ParseError<Loc, anyhow::Error>> {
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<AccountAddress, ParseError<Loc, anyhow::Error>> {
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 = {
// <n:Name> =>? Var::parse(n),
// };
Expand Down Expand Up @@ -741,6 +756,11 @@ fn parse_term_(tokens: &mut Lexer) -> Result<Exp_, ParseError<Loc, anyhow::Error
consume_token(tokens, Tok::RParen)?;
Ok(Exp_::ExprList(exps))
}
Tok::At => {
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),
Expand Down Expand Up @@ -1789,6 +1809,14 @@ fn parse_variant_decl(
// }

fn parse_module_ident(tokens: &mut Lexer) -> Result<ModuleIdent, ParseError<Loc, anyhow::Error>> {
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)?;
Expand Down Expand Up @@ -1914,9 +1942,16 @@ fn parse_module(tokens: &mut Lexer) -> Result<ModuleDefinition, ParseError<Loc,

pub fn parse_module_string(
input: &str,
) -> Result<ModuleDefinition, ParseError<Loc, anyhow::Error>> {
parse_module_string_with_named_addresses(input, &BTreeMap::new())
}

pub fn parse_module_string_with_named_addresses(
input: &str,
named_addresses: &BTreeMap<String, AccountAddress>,
) -> Result<ModuleDefinition, ParseError<Loc, anyhow::Error>> {
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)?;
Expand Down
14 changes: 13 additions & 1 deletion external-crates/move/crates/move-ir-to-bytecode/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<ast::ModuleDefinition> {
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<String, AccountAddress>,
) -> Result<ast::ModuleDefinition> {
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<T>(e: syntax::ParseError<Loc, anyhow::Error>, code_str: &str) -> Result<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,14 @@ pub fn compile_ir_module(
) -> Result<CompiledModule> {
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>(
Expand Down

0 comments on commit b5b0016

Please sign in to comment.