This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Metadata with complete type information #1328
Closed
Closed
Changes from all commits
Commits
Show all changes
88 commits
Select commit
Hold shift + click to select a range
de13508
almost compile
xlc 4ca30de
it compiles
xlc 3eec24d
trying to get wasm compile
xlc fd507ca
it should compile this time
xlc 350821d
wasm compiles
xlc 1446c3b
Encode primative types using enum instead of string
xlc 3638935
revert HasCompact changes
xlc 2f33f8e
Add compact type support
xlc 1564ee6
derive metadata for calls
xlc d6342e7
Merge branch 'master' into metadata-reflection-poc
xlc 203c0bf
support hash types
xlc 1ca6dec
EncodeMetadata for indices
xlc 80edbee
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc c536f4e
cargo check passed
xlc 1df0f62
wasm compiles
xlc e48bcba
wasm compile
xlc 455d4f2
add name
xlc 60bed38
MetadataRegistry refactor to handle circular type dependency
xlc 8037d80
all compiles
xlc 6cc547c
fix recursive dependency issue
xlc 8015a2d
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 7df5cb0
cleanup and have a single type registry
xlc 65d065d
update type metadata format
xlc bbf5366
start upgrading to new format
xlc 6fc5538
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc fd248ff
storage type metadata compiles
xlc fc91143
wip
xlc 7838464
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 757575f
actually compiles
xlc d538c59
fix typo and avoid save simple primitive types
xlc afe63ff
Avoid modify RuntimeMetadataV1, add RuntimeMetadataV2
xlc 89a9bd1
remove RuntimeMetadataV1
xlc 44ca4d1
output metadata name instead of string name
xlc 472f138
rename because there will be multiple type register methods
xlc fd540ac
add TODO
xlc 3a07a3f
Compac not only works for integer
xlc e3c9b68
wip
xlc c22b835
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc f866bbb
calls types compiles
xlc 5fd2aae
improve metadata bounds
xlc fceae4d
fix all build issues
xlc aab1fa7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc a9bf6ab
fix compile error after merge
xlc ca7266e
fix generic handling
xlc ac5a7c2
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 04c93be
fix comple error and bump version
xlc 398fc3d
remove debug print
xlc 3932cb4
fixed some tests
xlc 6eaa5e5
update wasm blob
xlc fda8c0d
use u16 over u32
xlc 9cde49d
fix build issue
xlc 2bbdb44
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc c8ff235
Revert "fix build issue"
xlc c32a2ab
fix build error
xlc f0c6553
fix build error
xlc 0153d46
fix warnings
xlc d6f937e
fix previous TODOs
xlc 688f65b
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 1e9bd60
move to rust 2018
xlc 3e7ee5c
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc cba200e
fix build issue
xlc 382d8d3
Revert "fix build error"
xlc ab8c67d
workaround $crate issue
xlc cfd3a05
fix dependency on vec
xlc 2b871e7
Make AccountId a new type so make it distinguishable from metadata
xlc dad29be
fix tests
xlc ea1e5d7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc b8fdb29
include blocks format in metadata
xlc e6f5e2c
fix tests
xlc 6e8ab45
devops-parity updated wasm runtime blobs 39ade79f and merged in maste…
devops-parity 0a8658c
support compac attribute
xlc 618db4e
Update comments
xlc 82a6dc7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc fbcdf4b
keep original type name as display_name
xlc 2143a7e
fix warning
xlc 7efbcb1
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 437244b
add module_path
xlc a191131
devops-parity updated wasm runtime blobs 4fc16063 and merged in maste…
devops-parity 5a05e5d
devops-parity updated wasm runtime blobs 1b92b125 and merged in maste…
devops-parity a1c9219
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 05ed4c0
fix build error and warnings
xlc bfe035a
Merge branch 'metadata-reflection-poc' of github.com:xlc/substrate in…
xlc d986bd5
fix test compiling error
xlc 7c4c9c3
fix build issue
xlc 121bede
revert account id change
xlc 682b823
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc 88275c0
fix
xlc 629eeae
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "substrate-metadata-derive" | ||
version = "0.1.0" | ||
authors = ["Bryan Chen <xlchen1291@gmail.com>"] | ||
edition = "2018" | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
syn = "0.15" | ||
quote = "0.6" | ||
proc-macro2 = "0.4" | ||
|
||
[features] | ||
default = ["std"] | ||
std = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
use quote::{quote, quote_spanned}; | ||
|
||
use proc_macro2::{Span, TokenStream}; | ||
use syn::{ | ||
Data, Field, Fields, Type, | ||
Meta, NestedMeta, Lit, Attribute, Variant, | ||
punctuated::Punctuated, | ||
spanned::Spanned, | ||
token::Comma, | ||
}; | ||
|
||
type FieldsList = Punctuated<Field, Comma>; | ||
|
||
fn encode_fields( | ||
fields: &FieldsList, | ||
registry: &TokenStream | ||
) -> TokenStream | ||
{ | ||
let recurse = fields.iter().enumerate().map(|(i, f)| { | ||
let compact = get_enable_compact(f); | ||
let name = f.ident.as_ref().map(|iden| quote! { | ||
_substrate_metadata::FieldName::Named(stringify!(#iden).into()) | ||
}) | ||
.unwrap_or(quote! { | ||
_substrate_metadata::FieldName::Unnamed(#i as u16) | ||
}); | ||
let ty = &f.ty; | ||
quote_spanned! { f.span() => | ||
{ | ||
let type_name = <#ty as _substrate_metadata::EncodeMetadata>::type_name(); | ||
#registry.register(type_name.clone(), <#ty as _substrate_metadata::EncodeMetadata>::type_metadata_kind); | ||
_substrate_metadata::FieldMetadata { | ||
name: #name, | ||
ty: if #compact { _substrate_metadata::MetadataName::Compact(Box::new(type_name)) } else { type_name } | ||
} | ||
} | ||
} | ||
}); | ||
|
||
quote! { | ||
_substrate_metadata::TypeMetadataKind::Struct(vec![#( #recurse, )*]) | ||
} | ||
} | ||
|
||
pub fn quote(data: &Data, registry: &TokenStream) -> TokenStream { | ||
let call_site = Span::call_site(); | ||
let res = match *data { | ||
Data::Struct(ref data) => { | ||
match data.fields { | ||
Fields::Named(ref fields) => encode_fields( | ||
&fields.named, | ||
registry | ||
), | ||
Fields::Unnamed(ref fields) => encode_fields( | ||
&fields.unnamed, | ||
registry | ||
), | ||
Fields::Unit => quote_spanned! { call_site => | ||
_substrate_metadata::TypeMetadataKind::Struct(vec![]) | ||
}, | ||
} | ||
}, | ||
Data::Enum(ref data) => { | ||
let recurse = data.variants.iter().enumerate().map(|(i, f)| { | ||
let name = &f.ident; | ||
let index = index(f, i); | ||
match f.fields { | ||
Fields::Named(ref fields) => { | ||
let field_name = |ty: &Type| { | ||
quote_spanned!(call_site => #ty) | ||
}; | ||
let fields = fields.named | ||
.iter() | ||
.map(|f| { | ||
let ty = field_name(&f.ty); | ||
let name = &f.ident; | ||
quote_spanned! { f.span() => | ||
{ | ||
let type_name = <#ty as _substrate_metadata::EncodeMetadata>::type_name(); | ||
#registry.register( | ||
type_name.clone(), | ||
<#ty as _substrate_metadata::EncodeMetadata>::type_metadata_kind | ||
); | ||
_substrate_metadata::FieldMetadata { | ||
name: _substrate_metadata::FieldName::Named(stringify!(#name).into()), | ||
ty: type_name | ||
} | ||
} | ||
} | ||
}); | ||
|
||
quote_spanned! { f.span() => | ||
_substrate_metadata::EnumVariantMetadata { | ||
name: stringify!(#name).into(), | ||
index: #index as u16, | ||
fields: vec![#( #fields, )*] | ||
} | ||
} | ||
}, | ||
Fields::Unnamed(ref fields) => { | ||
let field_name = |ty: &Type| { | ||
quote_spanned!(call_site => #ty) | ||
}; | ||
let fields = fields.unnamed | ||
.iter() | ||
.enumerate() | ||
.map(|(i, f)| { | ||
let ty = field_name(&f.ty); | ||
quote! { | ||
{ | ||
let type_name = <#ty as _substrate_metadata::EncodeMetadata>::type_name(); | ||
#registry.register( | ||
type_name.clone(), | ||
<#ty as _substrate_metadata::EncodeMetadata>::type_metadata_kind | ||
); | ||
_substrate_metadata::FieldMetadata { | ||
name: _substrate_metadata::FieldName::Unnamed(#i as u16), | ||
ty: type_name | ||
} | ||
} | ||
} | ||
}); | ||
|
||
quote_spanned! { f.span() => | ||
_substrate_metadata::EnumVariantMetadata { | ||
name: stringify!(#name).into(), | ||
index: #index as u16, | ||
fields: vec![#( #fields, )*] | ||
} | ||
} | ||
}, | ||
Fields::Unit => { | ||
quote_spanned! { f.span() => | ||
_substrate_metadata::EnumVariantMetadata { | ||
name: stringify!(#name).into(), | ||
index: #index as u16, | ||
fields: Vec::new() | ||
} | ||
} | ||
}, | ||
} | ||
}); | ||
|
||
quote! { | ||
_substrate_metadata::TypeMetadataKind::Enum(vec![#( #recurse, )*]) | ||
} | ||
}, | ||
Data::Union(_) => panic!("Union types are not supported."), | ||
}; | ||
res | ||
} | ||
|
||
fn find_meta_item<'a, F, R, I>(itr: I, pred: F) -> Option<R> where | ||
F: FnMut(&NestedMeta) -> Option<R> + Clone, | ||
I: Iterator<Item=&'a Attribute> | ||
{ | ||
itr.filter_map(|attr| { | ||
let pair = attr.path.segments.first()?; | ||
let seg = pair.value(); | ||
if seg.ident == "codec" { | ||
let meta = attr.interpret_meta(); | ||
if let Some(Meta::List(ref meta_list)) = meta { | ||
return meta_list.nested.iter().filter_map(pred.clone()).next(); | ||
} | ||
} | ||
|
||
None | ||
}).next() | ||
} | ||
|
||
fn index(v: &Variant, i: usize) -> TokenStream { | ||
// look for an index in attributes | ||
let index = find_meta_item(v.attrs.iter(), |meta| { | ||
if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta { | ||
if nv.ident == "index" { | ||
if let Lit::Str(ref s) = nv.lit { | ||
let byte: u8 = s.value().parse().expect("Numeric index expected."); | ||
return Some(byte) | ||
} | ||
} | ||
} | ||
|
||
None | ||
}); | ||
|
||
// then fallback to discriminant or just index | ||
index.map(|i| quote! { #i }) | ||
.unwrap_or_else(|| v.discriminant | ||
.as_ref() | ||
.map(|&(_, ref expr)| quote! { #expr }) | ||
.unwrap_or_else(|| quote! { #i }) | ||
) | ||
} | ||
|
||
fn get_enable_compact(field_entry: &Field) -> bool { | ||
// look for `encode(compact)` in the attributes | ||
find_meta_item(field_entry.attrs.iter(), |meta| { | ||
if let NestedMeta::Meta(Meta::Word(ref word)) = meta { | ||
if word == "compact" { | ||
return Some(()); | ||
} | ||
} | ||
|
||
None | ||
}).is_some() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright 2019 Parity Technologies (UK) Ltd. | ||
// This file is part of Substrate. | ||
|
||
// Substrate is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// Substrate is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Derives type metadata information to enable self-descriptive codec | ||
|
||
extern crate proc_macro; | ||
|
||
use syn::parse_quote; | ||
use quote::{quote}; | ||
|
||
use proc_macro::TokenStream; | ||
use proc_macro2::{Span}; | ||
use syn::{DeriveInput, Generics, Ident, GenericParam}; | ||
|
||
mod encode; | ||
|
||
#[proc_macro_derive(EncodeMetadata, attributes(codec))] | ||
pub fn encode_derive(input: TokenStream) -> TokenStream { | ||
let input: DeriveInput = match syn::parse(input) { | ||
Ok(input) => input, | ||
Err(e) => return e.to_compile_error().into(), | ||
}; | ||
|
||
let generics = add_trait_bounds(input.generics, parse_quote!(_substrate_metadata::EncodeMetadata)); | ||
|
||
let generic_types = generics.params.iter().map(|param| { | ||
if let GenericParam::Type(ref type_param) = *param { | ||
Some(type_param.ident.clone()) | ||
} else { | ||
None | ||
} | ||
}) | ||
.filter(Option::is_some) | ||
.collect::<Vec<_>>(); | ||
|
||
let name = &input.ident; | ||
let impl_type_name = if generic_types.is_empty() { | ||
quote! { | ||
fn type_name() -> _substrate_metadata::MetadataName { | ||
_substrate_metadata::MetadataName::Custom(module_path!().into(), stringify!(#name).into()) | ||
} | ||
} | ||
} else { | ||
let generic_type_names = generic_types.into_iter().map(|t| { | ||
quote! { | ||
<#t as _substrate_metadata::EncodeMetadata>::type_name() | ||
} | ||
}); | ||
quote! { | ||
fn type_name() -> _substrate_metadata::MetadataName { | ||
_substrate_metadata::MetadataName::CustomWithGenerics(module_path!().into(), stringify!(#name).into(), vec![ | ||
#( #generic_type_names ),* | ||
]) | ||
} | ||
} | ||
}; | ||
|
||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||
|
||
let registry = quote!(registry); | ||
|
||
let metadata_kind = encode::quote(&input.data, ®istry); | ||
|
||
let impl_block = quote! { | ||
impl #impl_generics _substrate_metadata::EncodeMetadata for #name #ty_generics #where_clause { | ||
#impl_type_name | ||
|
||
fn type_metadata_kind(registry: &mut _substrate_metadata::MetadataRegistry) -> _substrate_metadata::TypeMetadataKind { | ||
#metadata_kind | ||
} | ||
} | ||
}; | ||
|
||
let mut new_name = "_IMPL_ENCODEMETADATA_FOR_".to_string(); | ||
new_name.push_str(name.to_string().trim_start_matches("r#")); | ||
let dummy_const = Ident::new(&new_name, Span::call_site()); | ||
|
||
let generated = quote! { | ||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] | ||
const #dummy_const: () = { | ||
#[allow(unknown_lints)] | ||
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))] | ||
#[allow(rust_2018_idioms)] | ||
extern crate substrate_metadata as _substrate_metadata; | ||
use _substrate_metadata::rstd::prelude::*; | ||
#impl_block | ||
}; | ||
}; | ||
|
||
generated.into() | ||
} | ||
|
||
fn add_trait_bounds(mut generics: Generics, bounds: syn::TypeParamBound) -> Generics { | ||
for param in &mut generics.params { | ||
if let GenericParam::Type(ref mut type_param) = *param { | ||
type_param.bounds.push(bounds.clone()); | ||
} | ||
} | ||
generics | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no experience writing procedural macros. @rphmeier can you help review this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have much experience with proc-macros or this code in particular either.