diff --git a/crates/cairo-lang-compiler/src/db.rs b/crates/cairo-lang-compiler/src/db.rs index 33118e29693..e55ab6bec8f 100644 --- a/crates/cairo-lang-compiler/src/db.rs +++ b/crates/cairo-lang-compiler/src/db.rs @@ -100,10 +100,10 @@ impl RootDatabaseBuilder { pub fn with_inline_macro_plugin( &mut self, - name: String, + name: &str, plugin: Arc, ) -> &mut Self { - self.inline_macro_plugins.insert(name, plugin); + self.inline_macro_plugins.insert(name.into(), plugin); self } diff --git a/crates/cairo-lang-language-server/src/lib.rs b/crates/cairo-lang-language-server/src/lib.rs index a6f81855d6c..3671924f527 100644 --- a/crates/cairo-lang-language-server/src/lib.rs +++ b/crates/cairo-lang-language-server/src/lib.rs @@ -38,6 +38,7 @@ use cairo_lang_semantic::items::imp::ImplId; use cairo_lang_semantic::items::us::get_use_segments; use cairo_lang_semantic::resolve::{AsSegments, ResolvedConcreteItem, ResolvedGenericItem}; use cairo_lang_semantic::{SemanticDiagnostic, TypeLongId}; +use cairo_lang_starknet::inline_macros::selector::SelectorMacro; use cairo_lang_starknet::plugin::StarkNetPlugin; use cairo_lang_syntax::node::ast::PathSegment; use cairo_lang_syntax::node::helpers::GetIdentifier; @@ -81,6 +82,7 @@ pub async fn serve_language_service() { let db = RootDatabase::builder() .with_cfg(CfgSet::from_iter([Cfg::name("test")])) .with_macro_plugin(Arc::new(StarkNetPlugin::default())) + .with_inline_macro_plugin(SelectorMacro::NAME, Arc::new(SelectorMacro)) .build() .expect("Failed to initialize Cairo compiler database."); diff --git a/crates/cairo-lang-semantic/src/inline_macros/mod.rs b/crates/cairo-lang-semantic/src/inline_macros/mod.rs index b78071fe8a9..3cfb82bcbd6 100644 --- a/crates/cairo-lang-semantic/src/inline_macros/mod.rs +++ b/crates/cairo-lang-semantic/src/inline_macros/mod.rs @@ -21,7 +21,7 @@ pub fn get_default_inline_macro_plugins() -> OrderedHashMap InlinePluginResult { diff --git a/crates/cairo-lang-starknet/src/abi_test.rs b/crates/cairo-lang-starknet/src/abi_test.rs index aa6ff560199..5be2cd8331a 100644 --- a/crates/cairo-lang-starknet/src/abi_test.rs +++ b/crates/cairo-lang-starknet/src/abi_test.rs @@ -9,6 +9,7 @@ use indoc::indoc; use pretty_assertions::assert_eq; use crate::abi::AbiBuilder; +use crate::inline_macros::selector::SelectorMacro; use crate::plugin::StarkNetPlugin; #[test] @@ -201,6 +202,7 @@ fn test_abi_failure() { let db = &mut RootDatabase::builder() .detect_corelib() .with_macro_plugin(Arc::new(StarkNetPlugin::default())) + .with_inline_macro_plugin(SelectorMacro::NAME, Arc::new(SelectorMacro)) .build() .unwrap(); let module_id = setup_test_module( diff --git a/crates/cairo-lang-starknet/src/contract_class.rs b/crates/cairo-lang-starknet/src/contract_class.rs index 2105a190746..61d8ce66ad2 100644 --- a/crates/cairo-lang-starknet/src/contract_class.rs +++ b/crates/cairo-lang-starknet/src/contract_class.rs @@ -29,6 +29,7 @@ use crate::contract::{ find_contracts, get_module_abi_functions, get_selector_and_sierra_function, ContractDeclaration, }; use crate::felt252_serde::sierra_to_felt252s; +use crate::inline_macros::selector::SelectorMacro; use crate::plugin::consts::{CONSTRUCTOR_MODULE, EXTERNAL_MODULE, L1_HANDLER_MODULE}; use crate::plugin::StarkNetPlugin; @@ -85,6 +86,7 @@ pub fn compile_path( let mut db = RootDatabase::builder() .detect_corelib() .with_macro_plugin(Arc::new(StarkNetPlugin::default())) + .with_inline_macro_plugin(SelectorMacro::NAME, Arc::new(SelectorMacro)) .build()?; let main_crate_ids = setup_project(&mut db, Path::new(&path))?; diff --git a/crates/cairo-lang-starknet/src/inline_macros/mod.rs b/crates/cairo-lang-starknet/src/inline_macros/mod.rs new file mode 100644 index 00000000000..199a414becc --- /dev/null +++ b/crates/cairo-lang-starknet/src/inline_macros/mod.rs @@ -0,0 +1 @@ +pub mod selector; diff --git a/crates/cairo-lang-starknet/src/inline_macros/selector.rs b/crates/cairo-lang-starknet/src/inline_macros/selector.rs new file mode 100644 index 00000000000..79394e0029f --- /dev/null +++ b/crates/cairo-lang-starknet/src/inline_macros/selector.rs @@ -0,0 +1,46 @@ +use cairo_lang_defs::plugin::{InlineMacroExprPlugin, InlinePluginResult, PluginDiagnostic}; +use cairo_lang_semantic::inline_macros::unsupported_bracket_diagnostic; +use cairo_lang_syntax::node::db::SyntaxGroup; +use cairo_lang_syntax::node::{ast, TypedSyntaxNode}; + +use crate::contract::starknet_keccak; + +/// Macro for expanding a selector to a string literal. +#[derive(Debug, Default)] +pub struct SelectorMacro; +impl SelectorMacro { + pub const NAME: &'static str = "selector"; +} +impl InlineMacroExprPlugin for SelectorMacro { + fn generate_code( + &self, + db: &dyn SyntaxGroup, + syntax: &ast::ExprInlineMacro, + ) -> InlinePluginResult { + let ast::WrappedExprList::ParenthesizedExprList(args) = syntax.arguments(db) else { + return unsupported_bracket_diagnostic(db, syntax); + }; + + let arguments = &args.expressions(db).elements(db); + if arguments.len() != 1 { + let diagnostics = vec![PluginDiagnostic { + stable_ptr: syntax.stable_ptr().untyped(), + message: "selector macro must have a single argument".to_string(), + }]; + return InlinePluginResult { code: None, diagnostics }; + } + + let ast::Expr::String(input_string) = &arguments[0] else { + let diagnostics = vec![PluginDiagnostic { + stable_ptr: syntax.stable_ptr().untyped(), + message: "selector macro argument must be a string".to_string(), + }]; + return InlinePluginResult { code: None, diagnostics }; + }; + let selector_string = input_string.string_value(db).unwrap(); + + let selector = starknet_keccak(selector_string.as_bytes()); + let code: String = format!("0x{}", selector.to_str_radix(16)); + InlinePluginResult { code: Some(code), diagnostics: vec![] } + } +} diff --git a/crates/cairo-lang-starknet/src/lib.rs b/crates/cairo-lang-starknet/src/lib.rs index d4239e835ed..165612746f5 100644 --- a/crates/cairo-lang-starknet/src/lib.rs +++ b/crates/cairo-lang-starknet/src/lib.rs @@ -13,6 +13,7 @@ pub mod contract; pub mod contract_class; mod felt252_serde; mod felt252_vec_compression; +pub mod inline_macros; pub mod plugin; #[cfg(test)] diff --git a/crates/cairo-lang-starknet/src/plugin/dispatcher.rs b/crates/cairo-lang-starknet/src/plugin/dispatcher.rs index 7f0c15a93b9..288c24afad9 100644 --- a/crates/cairo-lang-starknet/src/plugin/dispatcher.rs +++ b/crates/cairo-lang-starknet/src/plugin/dispatcher.rs @@ -10,7 +10,6 @@ use itertools::Itertools; use super::consts::CALLDATA_PARAM_NAME; use super::utils::is_ref_param; use super::{DEPRECATED_ABI_ATTR, INTERFACE_ATTR}; -use crate::contract::starknet_keccak; /// If the trait is annotated with ABI_ATTR, generate the relevant dispatcher logic. pub fn handle_trait(db: &dyn SyntaxGroup, trait_ast: ast::ItemTrait) -> PluginResult { @@ -148,10 +147,8 @@ pub fn handle_trait(db: &dyn SyntaxGroup, trait_ast: ast::ItemTrait) -> PluginRe [("func_decl".to_string(), dispatcher_signature(db, &declaration, "T", false))] .into(), )); - let entry_point_selector = RewriteNode::Text(format!( - "0x{:x}", - starknet_keccak(declaration.name(db).text(db).as_bytes()) - )); + let entry_point_selector = + RewriteNode::Text(format!("selector!(\"{}\")", declaration.name(db).text(db))); contract_caller_method_impls.push(declaration_method_impl( dispatcher_signature(db, &declaration, &contract_caller_name, true), entry_point_selector.clone(), diff --git a/crates/cairo-lang-starknet/src/plugin/events.rs b/crates/cairo-lang-starknet/src/plugin/events.rs index efa195dd8c0..4002f864b49 100644 --- a/crates/cairo-lang-starknet/src/plugin/events.rs +++ b/crates/cairo-lang-starknet/src/plugin/events.rs @@ -14,7 +14,6 @@ use serde::{Deserialize, Serialize}; use smol_str::SmolStr; use super::aux_data::StarkNetEventAuxData; -use crate::contract::starknet_keccak; /// Generated auxiliary data for the `#[derive(starknet::Event)]` attribute. #[derive(Clone, Debug, PartialEq, Eq)] @@ -218,7 +217,6 @@ pub fn handle_enum(db: &dyn SyntaxGroup, enum_ast: ast::ItemEnum) -> PluginResul }; let variant_name = RewriteNode::new_trimmed(variant.name(db).as_syntax_node()); let name = variant.name(db).text(db); - let variant_selector = format!("0x{:x}", starknet_keccak(name.as_bytes())); let member_kind = get_field_kind_for_variant(db, &mut diagnostics, &variant, EventFieldKind::Nested); variants.push((name, member_kind)); @@ -226,12 +224,11 @@ pub fn handle_enum(db: &dyn SyntaxGroup, enum_ast: ast::ItemEnum) -> PluginResul let append_variant = RewriteNode::interpolate_patched( " $enum_name$::$variant_name$(val) => { - array::ArrayTrait::append(ref keys, $variant_selector$);$append_member$ + array::ArrayTrait::append(ref keys, selector!(\"$variant_name$\"));$append_member$ },", [ (String::from("enum_name"), enum_name.clone()), (String::from("variant_name"), variant_name.clone()), - (String::from("variant_selector"), RewriteNode::Text(variant_selector.clone())), (String::from("append_member"), append_member), ] .into(), @@ -239,13 +236,12 @@ pub fn handle_enum(db: &dyn SyntaxGroup, enum_ast: ast::ItemEnum) -> PluginResul let deserialize_member = deserialize_field(member_kind, RewriteNode::Text("val".into())); let deserialize_variant = RewriteNode::interpolate_patched( " - if selector == $variant_selector$ {$deserialize_member$ + if selector == selector!(\"$variant_name$\") {$deserialize_member$ return Option::Some($enum_name$::$variant_name$(val)); }", [ (String::from("enum_name"), enum_name.clone()), (String::from("variant_name"), variant_name.clone()), - (String::from("variant_selector"), RewriteNode::Text(variant_selector)), (String::from("deserialize_member"), deserialize_member), ] .into(), diff --git a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/components/component b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/components/component index 9bc49c41f19..0c4043517bd 100644 --- a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/components/component +++ b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/components/component @@ -177,7 +177,7 @@ impl EventIsEvent of starknet::Event { ) { match self { Event::Log(val) => { - array::ArrayTrait::append(ref keys, 0x100572688f36e5f96cee69badc6dcd8712fa19d323be018601b92e967c60678); + array::ArrayTrait::append(ref keys, selector!("Log")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); @@ -189,7 +189,7 @@ impl EventIsEvent of starknet::Event { ) -> Option { let selector = *array::SpanTrait::pop_front(ref keys)?; - if selector == 0x100572688f36e5f96cee69badc6dcd8712fa19d323be018601b92e967c60678 { + if selector == selector!("Log") { let val = starknet::Event::deserialize( ref keys, ref data )?; diff --git a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/contract b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/contract index 47c0a5c4bb8..d89b858ac2a 100644 --- a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/contract +++ b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/contract @@ -280,13 +280,13 @@ impl EventIsEvent of starknet::Event { ) { match self { Event::AwesomeEvent(val) => { - array::ArrayTrait::append(ref keys, 0x2fbea1ee750e49ff764a5c7183b4201cc4beb8ead010fbb75b671e6f31bb82); + array::ArrayTrait::append(ref keys, selector!("AwesomeEvent")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); }, Event::BestEventEver(val) => { - array::ArrayTrait::append(ref keys, 0x7b89143e59dd87ddfbccfa6075ca9296b9b5f26183bc738db54c2313fab4ed); + array::ArrayTrait::append(ref keys, selector!("BestEventEver")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); @@ -298,13 +298,13 @@ impl EventIsEvent of starknet::Event { ) -> Option { let selector = *array::SpanTrait::pop_front(ref keys)?; - if selector == 0x2fbea1ee750e49ff764a5c7183b4201cc4beb8ead010fbb75b671e6f31bb82 { + if selector == selector!("AwesomeEvent") { let val = starknet::Event::deserialize( ref keys, ref data )?; return Option::Some(Event::AwesomeEvent(val)); } - if selector == 0x7b89143e59dd87ddfbccfa6075ca9296b9b5f26183bc738db54c2313fab4ed { + if selector == selector!("BestEventEver") { let val = starknet::Event::deserialize( ref keys, ref data )?; diff --git a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/dispatcher b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/dispatcher index 7d6b636d91e..fb8e96baf1e 100644 --- a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/dispatcher +++ b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/dispatcher @@ -41,7 +41,7 @@ impl IContractDispatcherImpl of IContractDispatcherTrait { let mut ret_data = starknet::call_contract_syscall( self.contract_address, - 0x3c52d61651de3dcab6ceaa9f6505f7aed8f1ffc0f694ce2a9ed76e758d87a3, + selector!("get_something"), array::ArrayTrait::span(@__calldata__), ); let mut ret_data = starknet::SyscallResultTrait::unwrap_syscall(ret_data); @@ -56,7 +56,7 @@ impl IContractDispatcherImpl of IContractDispatcherTrait { let mut ret_data = starknet::call_contract_syscall( self.contract_address, - 0x1fc3f77ebc090777f567969ad9823cf6334ab888acb385ca72668ec5adbde80, + selector!("empty"), array::ArrayTrait::span(@__calldata__), ); let mut ret_data = starknet::SyscallResultTrait::unwrap_syscall(ret_data); @@ -78,7 +78,7 @@ impl IContractLibraryDispatcherImpl of IContractDispatcherTrait { ) { match self { MyEventEnum::A(val) => { - array::ArrayTrait::append(ref keys, 0x3783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760); + array::ArrayTrait::append(ref keys, selector!("A")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); }, MyEventEnum::B(val) => { - array::ArrayTrait::append(ref keys, 0x3675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111); + array::ArrayTrait::append(ref keys, selector!("B")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); @@ -182,13 +182,13 @@ impl MyEventEnumIsEvent of starknet::Event { ) -> Option { let selector = *array::SpanTrait::pop_front(ref keys)?; - if selector == 0x3783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760 { + if selector == selector!("A") { let val = starknet::Event::deserialize( ref keys, ref data )?; return Option::Some(MyEventEnum::A(val)); } - if selector == 0x3675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111 { + if selector == selector!("B") { let val = starknet::Event::deserialize( ref keys, ref data )?; diff --git a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/external_event b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/external_event index 2b13370b2c6..c8dd69c741d 100644 --- a/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/external_event +++ b/crates/cairo-lang-starknet/src/plugin/plugin_test_data/contracts/external_event @@ -66,13 +66,13 @@ impl EventIsEvent of starknet::Event { ) { match self { Event::AwesomeEvent(val) => { - array::ArrayTrait::append(ref keys, 0x2fbea1ee750e49ff764a5c7183b4201cc4beb8ead010fbb75b671e6f31bb82); + array::ArrayTrait::append(ref keys, selector!("AwesomeEvent")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); }, Event::BestEventEver(val) => { - array::ArrayTrait::append(ref keys, 0x7b89143e59dd87ddfbccfa6075ca9296b9b5f26183bc738db54c2313fab4ed); + array::ArrayTrait::append(ref keys, selector!("BestEventEver")); starknet::Event::append_keys_and_data( val, ref keys, ref data ); @@ -84,13 +84,13 @@ impl EventIsEvent of starknet::Event { ) -> Option { let selector = *array::SpanTrait::pop_front(ref keys)?; - if selector == 0x2fbea1ee750e49ff764a5c7183b4201cc4beb8ead010fbb75b671e6f31bb82 { + if selector == selector!("AwesomeEvent") { let val = starknet::Event::deserialize( ref keys, ref data )?; return Option::Some(Event::AwesomeEvent(val)); } - if selector == 0x7b89143e59dd87ddfbccfa6075ca9296b9b5f26183bc738db54c2313fab4ed { + if selector == selector!("BestEventEver") { let val = starknet::Event::deserialize( ref keys, ref data )?; diff --git a/crates/cairo-lang-starknet/src/test_utils.rs b/crates/cairo-lang-starknet/src/test_utils.rs index 94f475ac6c9..0777afcdb1a 100644 --- a/crates/cairo-lang-starknet/src/test_utils.rs +++ b/crates/cairo-lang-starknet/src/test_utils.rs @@ -11,6 +11,7 @@ use once_cell::sync::Lazy; use crate::allowed_libfuncs::BUILTIN_ALL_LIBFUNCS_LIST; use crate::contract_class::compile_contract_in_prepared_db; +use crate::inline_macros::selector::SelectorMacro; use crate::plugin::StarkNetPlugin; /// Returns a path to example contract that matches `name`. @@ -27,6 +28,7 @@ pub static SHARED_DB: Lazy> = Lazy::new(|| { RootDatabase::builder() .detect_corelib() .with_macro_plugin(Arc::new(StarkNetPlugin::default())) + .with_inline_macro_plugin(SelectorMacro::NAME, Arc::new(SelectorMacro)) .build() .unwrap(), ) diff --git a/crates/cairo-lang-test-runner/src/lib.rs b/crates/cairo-lang-test-runner/src/lib.rs index 2bebafb78a4..d4194896262 100644 --- a/crates/cairo-lang-test-runner/src/lib.rs +++ b/crates/cairo-lang-test-runner/src/lib.rs @@ -26,6 +26,7 @@ use cairo_lang_starknet::casm_contract_class::ENTRY_POINT_COST; use cairo_lang_starknet::contract::{ find_contracts, get_contracts_info, get_module_abi_functions, ContractInfo, }; +use cairo_lang_starknet::inline_macros::selector::SelectorMacro; use cairo_lang_starknet::plugin::consts::{CONSTRUCTOR_MODULE, EXTERNAL_MODULE, L1_HANDLER_MODULE}; use cairo_lang_starknet::plugin::StarkNetPlugin; use cairo_lang_utils::casts::IntoOrPanic; @@ -76,7 +77,8 @@ impl TestRunner { b.with_macro_plugin(Arc::new(TestPlugin::default())); if starknet { - b.with_macro_plugin(Arc::new(StarkNetPlugin::default())); + b.with_macro_plugin(Arc::new(StarkNetPlugin::default())) + .with_inline_macro_plugin(SelectorMacro::NAME, Arc::new(SelectorMacro)); } b.build()?