Skip to content

Commit

Permalink
refactor: use attr sig info in abi generator (#1036)
Browse files Browse the repository at this point in the history
* test: add test coverage for abi generation (Fixes #1031)
* docs: fix abi generation example
* refactor: add `is_private` to attr sig info
* refactor: switch to attr sig info v2 in abi generator
* refactor: remove attr sig info v1
* refactor: remove unused clone implementations
  • Loading branch information
agostbiro committed Jun 29, 2023
1 parent c39fba3 commit 6887b41
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 218 deletions.
358 changes: 284 additions & 74 deletions near-sdk-macros/src/core_impl/abi/abi_generator.rs

Large diffs are not rendered by default.

31 changes: 28 additions & 3 deletions near-sdk-macros/src/core_impl/code_generator/attr_sig_info.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
use proc_macro2::TokenStream as TokenStream2;

use crate::core_impl::info_extractor::{ArgInfo, AttrSigInfoV2, BindgenArgType, SerializerType};
use crate::core_impl::utils;
use crate::core_impl::info_extractor::{ArgInfo, AttrSigInfo, BindgenArgType, SerializerType};
use crate::core_impl::{utils, MethodKind};
use quote::quote;

impl AttrSigInfoV2 {
impl AttrSigInfo {
/// Whether the signature has function arguments.
pub fn has_input_args(&self) -> bool {
self.input_args().next().is_some()
}

/// Whether the method has `payable` attribute.
/// Only available when `__abi-generate` feature is enabled as it's only in the abi generator
/// currently.
#[cfg(feature = "__abi-generate")]
pub fn is_payable(&self) -> bool {
use MethodKind::*;

match &self.method_kind {
Call(call_method) => call_method.is_payable,
Init(init_method) => init_method.is_payable,
View(_) => false,
}
}

/// Whether the method has `private` attribute.
pub fn is_private(&self) -> bool {
use MethodKind::*;

match &self.method_kind {
Call(call_method) => call_method.is_private,
Init(_) => false,
View(view_method) => view_method.is_private,
}
}

pub fn input_struct_ser(&self) -> TokenStream2 {
let args: Vec<_> = self.input_args().collect();
assert!(
Expand Down
8 changes: 4 additions & 4 deletions near-sdk-macros/src/core_impl/code_generator/ext.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::core_impl::{serializer, AttrSigInfoV2};
use crate::core_impl::{serializer, AttrSigInfo};
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, ToTokens};
use syn::{parse_quote, Attribute, Generics, Path, Signature};
Expand Down Expand Up @@ -97,7 +97,7 @@ fn is_fn_attribute_to_forward(attribute: &Attribute) -> bool {
/// Generate methods on <StructName>Ext to enable calling each method.
pub(crate) fn generate_ext_function_wrappers<'a>(
ident: &Ident,
methods: impl IntoIterator<Item = &'a AttrSigInfoV2>,
methods: impl IntoIterator<Item = &'a AttrSigInfo>,
) -> TokenStream2 {
let ext_ident = format_ident!("{}Ext", ident);
let mut res = TokenStream2::new();
Expand All @@ -111,12 +111,12 @@ pub(crate) fn generate_ext_function_wrappers<'a>(
}
}

fn generate_ext_function(attr_signature_info: &AttrSigInfoV2) -> TokenStream2 {
fn generate_ext_function(attr_signature_info: &AttrSigInfo) -> TokenStream2 {
let pat_type_list = attr_signature_info.pat_type_list();
let serialize =
serializer::generate_serializer(attr_signature_info, &attr_signature_info.input_serializer);

let AttrSigInfoV2 { non_bindgen_attrs, ident, original_sig, .. } = attr_signature_info;
let AttrSigInfo { non_bindgen_attrs, ident, original_sig, .. } = attr_signature_info;
let ident_str = ident.to_string();
let mut new_non_bindgen_attrs = TokenStream2::new();
for attribute in non_bindgen_attrs.iter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,36 +171,15 @@ impl ImplItemMethodInfo {
}

fn private_check_tokens(&self) -> TokenStream2 {
use MethodKind::*;

let reject_cross_call = || {
if self.attr_signature_info.is_private() {
let error = format!("Method {} is private", self.attr_signature_info.ident);
quote! {
if near_sdk::env::current_account_id() != near_sdk::env::predecessor_account_id() {
near_sdk::env::panic_str(#error);
}
}
};

match &self.attr_signature_info.method_kind {
Call(call_method) => {
if call_method.is_private {
reject_cross_call()
} else {
quote! {}
}
}

// Init methods cannot be private.
Init(_) => quote! {},

View(view_method) => {
if view_method.is_private {
reject_cross_call()
} else {
quote! {}
}
}
} else {
quote! {}
}
}

Expand Down
4 changes: 2 additions & 2 deletions near-sdk-macros/src/core_impl/code_generator/serializer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::core_impl::info_extractor::{AttrSigInfoV2, SerializerType};
use crate::core_impl::info_extractor::{AttrSigInfo, SerializerType};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

pub fn generate_serializer(
attr_sig_info: &AttrSigInfoV2,
attr_sig_info: &AttrSigInfo,
serializer: &SerializerType,
) -> TokenStream2 {
let has_input_args = attr_sig_info.input_args().next().is_some();
Expand Down
2 changes: 0 additions & 2 deletions near-sdk-macros/src/core_impl/info_extractor/arg_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{spanned::Spanned, Attribute, Error, Ident, Pat, PatType, Token, Type};

#[derive(Clone)]
pub enum BindgenArgType {
/// Argument that we read from `env::input()`.
Regular,
Expand All @@ -17,7 +16,6 @@ pub enum BindgenArgType {
}

/// A single argument of a function after it was processed by the bindgen.
#[derive(Clone)]
pub struct ArgInfo {
/// Attributes not related to bindgen.
pub non_bindgen_attrs: Vec<Attribute>,
Expand Down
97 changes: 5 additions & 92 deletions near-sdk-macros/src/core_impl/info_extractor/attr_sig_info.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use super::visitor::Visitor;
use super::{
ArgInfo, BindgenArgType, InitAttr, MethodKind, MethodType, ReturnKind, SerializerAttr,
SerializerType,
};
use super::{ArgInfo, BindgenArgType, InitAttr, MethodKind, SerializerAttr, SerializerType};
use crate::core_impl::{utils, Returns};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::ToTokens;
use syn::spanned::Spanned;
use syn::{Attribute, Error, FnArg, GenericParam, Ident, Receiver, ReturnType, Signature, Type};
use syn::{Attribute, Error, FnArg, GenericParam, Ident, ReturnType, Signature, Type};

/// Information extracted from method attributes and signature.
#[derive(Clone)]
pub struct AttrSigInfoV2 {
pub struct AttrSigInfo {
/// The name of the method.
pub ident: Ident,
/// Attributes not related to bindgen.
Expand All @@ -28,90 +24,7 @@ pub struct AttrSigInfoV2 {
pub original_sig: Signature,
}

/// Information extracted from method attributes and signature.
pub struct AttrSigInfoV1 {
/// The name of the method.
pub ident: Ident,
/// Attributes not related to bindgen.
pub non_bindgen_attrs: Vec<Attribute>,
/// All arguments of the method.
pub args: Vec<ArgInfo>,
/// Describes the type of the method.
pub method_type: MethodType,
/// Whether method accepting $NEAR.
pub is_payable: bool,
/// Whether method can accept calls from self (current account)
pub is_private: bool,
/// Whether method returns Result type where only Ok type is serialized
pub is_handles_result: bool,
/// The serializer that we use for `env::input()`.
pub input_serializer: SerializerType,
/// The serializer that we use for the return type.
pub result_serializer: SerializerType,
/// The receiver, like `mut self`, `self`, `&mut self`, `&self`, or `None`.
pub receiver: Option<Receiver>,
/// What this function returns.
pub returns: ReturnType,
/// The original method signature.
pub original_sig: Signature,
}

// FIXME: Remove once we refactor ABI generator to use `AttrSigInfoV2`
// Tracking issue: https://github.com/near/near-sdk-rs/issues/1032
impl From<AttrSigInfoV2> for AttrSigInfoV1 {
fn from(info: AttrSigInfoV2) -> Self {
match info.method_kind {
MethodKind::Call(call_method) => AttrSigInfoV1 {
ident: info.ident,
non_bindgen_attrs: info.non_bindgen_attrs,
args: info.args,
method_type: MethodType::Regular,
is_payable: call_method.is_payable,
is_private: call_method.is_private,
is_handles_result: matches!(info.returns.kind, ReturnKind::HandlesResult { .. }),
input_serializer: info.input_serializer,
result_serializer: call_method.result_serializer,
receiver: call_method.receiver,
returns: info.returns.original,
original_sig: info.original_sig,
},
MethodKind::View(view_method) => AttrSigInfoV1 {
ident: info.ident,
non_bindgen_attrs: info.non_bindgen_attrs,
args: info.args,
method_type: MethodType::View,
is_payable: false,
is_private: view_method.is_private,
is_handles_result: matches!(info.returns.kind, ReturnKind::HandlesResult { .. }),
input_serializer: info.input_serializer,
result_serializer: view_method.result_serializer,
receiver: view_method.receiver,
returns: info.returns.original,
original_sig: info.original_sig,
},
MethodKind::Init(init_method) => AttrSigInfoV1 {
ident: info.ident,
non_bindgen_attrs: info.non_bindgen_attrs,
args: info.args,
method_type: if init_method.ignores_state {
MethodType::InitIgnoreState
} else {
MethodType::Init
},
is_payable: init_method.is_payable,
is_private: false,
is_handles_result: matches!(info.returns.kind, ReturnKind::HandlesResult { .. }),
input_serializer: info.input_serializer,
result_serializer: SerializerType::JSON,
receiver: None,
returns: info.returns.original,
original_sig: info.original_sig,
},
}
}
}

impl AttrSigInfoV2 {
impl AttrSigInfo {
/// Apart from replacing `Self` types with their concretions, returns spans of all `Self` tokens found.
fn sanitize_self(
original_sig: &mut Signature,
Expand Down Expand Up @@ -235,7 +148,7 @@ impl AttrSigInfoV2 {
//
}

let mut result = AttrSigInfoV2 {
let mut result = AttrSigInfo {
ident,
non_bindgen_attrs,
args,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::core_impl::info_extractor::AttrSigInfoV2;
use crate::core_impl::info_extractor::AttrSigInfo;
use crate::core_impl::utils;
use quote::ToTokens;
use syn::{ImplItemMethod, Type, Visibility};

/// Information extracted from `ImplItemMethod`.
pub struct ImplItemMethodInfo {
/// Information on the attributes and the signature of the method.
pub attr_signature_info: AttrSigInfoV2,
pub attr_signature_info: AttrSigInfo,
/// The type of the contract struct.
pub struct_type: Type,
}
Expand All @@ -21,8 +21,7 @@ impl ImplItemMethodInfo {
let ImplItemMethod { attrs, sig, .. } = original;
utils::sig_is_supported(sig)?;
if is_trait_impl || matches!(original.vis, Visibility::Public(_)) {
let attr_signature_info =
AttrSigInfoV2::new(attrs, sig, &struct_type.to_token_stream())?;
let attr_signature_info = AttrSigInfo::new(attrs, sig, &struct_type.to_token_stream())?;
Ok(Some(Self { attr_signature_info, struct_type }))
} else {
Ok(None)
Expand Down
11 changes: 1 addition & 10 deletions near-sdk-macros/src/core_impl/info_extractor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod arg_info;
pub use arg_info::{ArgInfo, BindgenArgType};

mod attr_sig_info;
pub use attr_sig_info::{AttrSigInfoV1, AttrSigInfoV2};
pub use attr_sig_info::AttrSigInfo;

mod impl_item_method_info;
pub use impl_item_method_info::ImplItemMethodInfo;
Expand Down Expand Up @@ -35,15 +35,6 @@ pub enum SerializerType {
Borsh,
}

/// Type of the method.
#[derive(PartialEq, Eq)]
pub enum MethodType {
Regular,
View,
Init,
InitIgnoreState,
}

#[derive(Clone, PartialEq, Eq)]
pub enum MethodKind {
Call(CallMethod),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::AttrSigInfoV2;
use super::AttrSigInfo;
use crate::core_impl::utils;
use proc_macro2::TokenStream as TokenStream2;
use syn::spanned::Spanned;
Expand All @@ -7,7 +7,7 @@ use syn::{Error, LitStr, TraitItemMethod};
/// Information extracted from trait method.
pub struct TraitItemMethodInfo {
/// Attributes and signature information.
pub attr_sig_info: AttrSigInfoV2,
pub attr_sig_info: AttrSigInfo,
/// The original AST of the trait item method.
pub original: TraitItemMethod,
/// String representation of method name, e.g. `"my_method"`.
Expand All @@ -28,7 +28,7 @@ impl TraitItemMethodInfo {
let TraitItemMethod { attrs, sig, .. } = original;

utils::sig_is_supported(sig)?;
let attr_sig_info = AttrSigInfoV2::new(attrs, sig, trait_name)?;
let attr_sig_info = AttrSigInfo::new(attrs, sig, trait_name)?;

let ident_byte_str =
LitStr::new(&attr_sig_info.ident.to_string(), attr_sig_info.ident.span());
Expand Down

0 comments on commit 6887b41

Please sign in to comment.