Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: near_abi! macro #831

Merged
merged 36 commits into from
Jun 23, 2022
Merged

feat: near_abi! macro #831

merged 36 commits into from
Jun 23, 2022

Conversation

itegulov
Copy link
Contributor

@itegulov itegulov commented Jun 3, 2022

Basically just the macro and ABI models without all other extra stuff from daniyar/aci like cargo extension, cross contract calls etc.

We have not decided in what branch this should live in, so I am targeting master for now.

examples/abi/README.md Outdated Show resolved Hide resolved
examples/abi/res/abi.json Outdated Show resolved Hide resolved
const _: () = {
#[no_mangle]
#[cfg(not(target_arch = "wasm32"))]
pub fn __near_abi() -> near_sdk::__private::AbiRoot {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this will fail to compile when there are multiple impl sections with near_bindgen, right? Will double check tomorrow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it should not as long as all impl sections are wrapped in the same near_abi! call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't realize that case is handled. It limits the ability to have impl sections in diff modules, though

Copy link
Contributor Author

@itegulov itegulov Jun 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I am unsure how to handle this though... I have tried depending on modules like this:

#[near_abi]
mod abi {
    #[path = "utility.rs"]
    mod abi_utility;

    #[near_bindgen]
    #[derive(Default, BorshDeserialize, BorshSerialize)]
    pub struct Contract {}

    ...
}

But apparently including non-inline modules in proc macro input is an unstable feature.

I was thinking I could maybe create a global state that would be modified by all #[near_abi] annotations and then add another macro that would output the accumulated state into one ABI structure. The problem is that I think there is no way to enforce macro execution order. Do you have any ideas I could explore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another very vague idea I have considered is to generate a bunch of symbols (e.g. __near_abi_lib_rs, __near_abi_utility_rs etc) and then somehow get a list of them from cargo extension and call all of them. The main problem here is that I cannot find if there is a rustc/cargo API that would allow me to list all symbols from a crate.

near-sdk-macros/src/core_impl/abi/mod.rs Outdated Show resolved Hide resolved
near-sdk/src/private/abi.rs Outdated Show resolved Hide resolved
near-sdk/src/private/abi.rs Outdated Show resolved Hide resolved
}

fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
T::json_schema(gen)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that right now nothing restricts a developer from having the promise return a different schema type than T.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I also thought about this. Returning different things feels like a strange pattern to me, but I guess since we are not discouraging this pattern I should not presume that there is only one possible return type. I could represent this as an equivalent of Either<Any, T> or just simply Any. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a Result<T> since this is looks to be fallible and we're expecting a type T. Maybe not for this function, but another one for that can return a Result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is not really fallible as it is non-restrictive to just T, so I don't think Result suits unless I am missing something here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that making the assumption as one type is fine, it would nudge developers in the correct pattern, but I'm just making the comment so we don't ignore the cases where maybe someone might return different types that serialize as the same thing. Can't immediately think of an implication that would be an issue, though

examples/abi/src/lib.rs Outdated Show resolved Hide resolved
near-sdk-macros/src/core_impl/abi/abi_visitor.rs Outdated Show resolved Hide resolved
@austinabell
Copy link
Contributor

austinabell commented Jun 6, 2022

We have not decided in what branch this should live in, so I am targeting master for now.

Hmm, yeah either master becomes the 5.0 path and we backport non-breaking changes to some 4.x branch, or we put these changes into a branch that targets the main branch and only merge those commits in when we are ready to release the next version. Seems like maybe the first option might be cleaner, and then we can just cherry-pick commits when we want to release any 4.x branch. Might be more work, but seems okay

@itegulov
Copy link
Contributor Author

itegulov commented Jun 6, 2022

We have not decided in what branch this should live in, so I am targeting master for now.

Hmm, yeah either master becomes the 5.0 path and we backport non-breaking changes to some 4.x branch, or we put these changes into a branch that targets the main branch and only merge those commits in when we are ready to release the next version. Seems like maybe the first option might be cleaner, and then we can just cherry-pick commits when we want to release any 4.x branch. Might be more work, but seems okay

Yeah sounds good to me.

Do we want to put this macro and ABI models under unstable for now?

}

fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
T::json_schema(gen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a Result<T> since this is looks to be fallible and we're expecting a type T. Maybe not for this function, but another one for that can return a Result

near-sdk/src/private/abi.rs Outdated Show resolved Hide resolved
@austinabell
Copy link
Contributor

Do we want to put this macro and ABI models under unstable for now?

Yes, please.

near-sdk/src/lib.rs Outdated Show resolved Hide resolved
.attr_signature_info
.args
.iter()
.filter(|arg| matches!(arg.bindgen_ty, BindgenArgType::CallbackArg))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handles callback, but does not handle CallbackResultArg. Am I missing something with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah, you are right. I took it from here: https://github.com/near/near-sdk-rs/blob/master/near-sdk-macros/src/core_impl/metadata/metadata_generator.rs#L69, so I guess the previous metadata implementation is not accounting for this case either. Should I fix it in a separate PR or do you think it is inconsequential at this point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it depends what we do with metadata, but my assumption is that it would be deprecated.

As for callback result, will this not have to be handled more specifically than ccfba2e (#831) to ignore the wrapping Result type in the schema?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good catch. I have refactored the entire argument processing flow because it was getting out of hand, this case should be handled now.

examples/abi/Cargo.toml Show resolved Hide resolved
examples/abi/src/lib.rs Outdated Show resolved Hide resolved
examples/abi/src/lib.rs Outdated Show resolved Hide resolved
near-sdk/Cargo.toml Outdated Show resolved Hide resolved
Comment on lines 77 to 78
#[doc(hidden)]
pub use schemars;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just remove this altogether, re-exporting things like this is generally a bad pattern and the others would be purged if it wasn't very breaking of a change

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting again this would be ideal not to export and require that we keep this API stable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, moved behind __private for now since I can't figure out how to remove it

@itegulov itegulov force-pushed the daniyar/abi-macro branch from bc59ec3 to ddc6824 Compare June 16, 2022 01:09
@itegulov
Copy link
Contributor Author

@austinabell so after our last conversation I am not sure if it makes sense to leave abi under unstable feature now. My vision is that there is going to be an abi feature that controls whether near_bindgen generates ABI symbols. So I don't know if it makes sense to bundle it up with unstable, should I create something like abi_unstable for it for now?

@austinabell
Copy link
Contributor

austinabell commented Jun 21, 2022

@austinabell so after our last conversation I am not sure if it makes sense to leave abi under unstable feature now. My vision is that there is going to be an abi feature that controls whether near_bindgen generates ABI symbols. So I don't know if it makes sense to bundle it up with unstable, should I create something like abi_unstable for it for now?

no, if we are adding a feature, it should be something long-term because removing is unergonomic for anyone who specified it previously (breaking change). If you wanted it to be abi_unstable, instead you can just require someone enable the abi feature and unstable. Up to you which direction this goes into between putting it under abi, unstable, or both. My guess is abi is probably the best, we just need to make sure that name is stable and set before releasing the next SDK version

@itegulov
Copy link
Contributor Author

no, if we are adding a feature, it should be something long-term because removing is unergonomic for anyone who specified it previously (breaking change). If you wanted it to be abi_unstable, instead you can just require someone enable the abi feature and unstable. Up to you which direction this goes into between putting it under abi, unstable, or both. My guess is abi is probably the best, we just need to make sure that name is stable and set before releasing the next SDK version

Makes sense, I am putting everything behind abi then.

Copy link
Contributor

@austinabell austinabell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks generally good! Just a few small comments and should be good

/// If args are serialized with Borsh it will not include `#[derive(borsh::BorshSchema)]`.
pub fn abi_struct(&self) -> TokenStream2 {
let function_name_str = self.attr_signature_info.ident.to_string();
let is_view = matches!(&self.attr_signature_info.method_type, &MethodType::View);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we should start thinking of ways to avoid false positives for this. The MethodType::View is just used for codegen, but can't reliably indicate a function is view externally. Wonder if analysis of the wasm binary to see if any non-view functions are used (state write, promises) will be necessary.

This is probably fine for now, though. We should open an issue for this before or after this comes in

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, created an issue: #850

near-sdk-macros/src/core_impl/abi/abi_generator.rs Outdated Show resolved Hide resolved
near-sdk-macros/src/core_impl/abi/abi_visitor.rs Outdated Show resolved Hide resolved
near-sdk/src/private/abi.rs Outdated Show resolved Hide resolved
near-sdk/src/private/abi.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@austinabell austinabell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, one thing I overlooked and then good

const _: () = {
#[no_mangle]
pub fn #near_abi_symbol() -> near_sdk::__private::AbiRoot {
let mut gen = schemars::gen::SchemaGenerator::default();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should use the re-export from the SDK. Ideally behind a private module. This forces a developer to import schemars even if they don't use

This should be near_sdk::__private::schemars:: or near_sdk::schemars:: if that re-export is not moved from other comment

Comment on lines 77 to 78
#[doc(hidden)]
pub use schemars;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting again this would be ideal not to export and require that we keep this API stable

@itegulov itegulov merged commit 9ab6df8 into master Jun 23, 2022
@itegulov itegulov deleted the daniyar/abi-macro branch June 23, 2022 23:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants