Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Metadata with complete type information #1328

Closed
wants to merge 88 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
de13508
almost compile
xlc Dec 23, 2018
4ca30de
it compiles
xlc Dec 24, 2018
3eec24d
trying to get wasm compile
xlc Dec 24, 2018
fd507ca
it should compile this time
xlc Dec 24, 2018
350821d
wasm compiles
xlc Dec 24, 2018
1446c3b
Encode primative types using enum instead of string
xlc Dec 26, 2018
3638935
revert HasCompact changes
xlc Jan 2, 2019
2f33f8e
Add compact type support
xlc Jan 3, 2019
1564ee6
derive metadata for calls
xlc Jan 3, 2019
d6342e7
Merge branch 'master' into metadata-reflection-poc
xlc Jan 22, 2019
203c0bf
support hash types
xlc Jan 22, 2019
1ca6dec
EncodeMetadata for indices
xlc Jan 22, 2019
80edbee
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Jan 23, 2019
c536f4e
cargo check passed
xlc Jan 23, 2019
1df0f62
wasm compiles
xlc Jan 23, 2019
e48bcba
wasm compile
xlc Jan 24, 2019
455d4f2
add name
xlc Jan 24, 2019
60bed38
MetadataRegistry refactor to handle circular type dependency
xlc Jan 24, 2019
8037d80
all compiles
xlc Jan 25, 2019
6cc547c
fix recursive dependency issue
xlc Jan 25, 2019
8015a2d
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Jan 31, 2019
7df5cb0
cleanup and have a single type registry
xlc Jan 31, 2019
65d065d
update type metadata format
xlc Jan 31, 2019
bbf5366
start upgrading to new format
xlc Feb 1, 2019
6fc5538
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 1, 2019
fd248ff
storage type metadata compiles
xlc Feb 3, 2019
fc91143
wip
xlc Feb 3, 2019
7838464
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 4, 2019
757575f
actually compiles
xlc Feb 4, 2019
d538c59
fix typo and avoid save simple primitive types
xlc Feb 5, 2019
afe63ff
Avoid modify RuntimeMetadataV1, add RuntimeMetadataV2
xlc Feb 5, 2019
89a9bd1
remove RuntimeMetadataV1
xlc Feb 5, 2019
44ca4d1
output metadata name instead of string name
xlc Feb 5, 2019
472f138
rename because there will be multiple type register methods
xlc Feb 5, 2019
fd540ac
add TODO
xlc Feb 5, 2019
3a07a3f
Compac not only works for integer
xlc Feb 5, 2019
e3c9b68
wip
xlc Feb 5, 2019
c22b835
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 6, 2019
f866bbb
calls types compiles
xlc Feb 8, 2019
5fd2aae
improve metadata bounds
xlc Feb 9, 2019
fceae4d
fix all build issues
xlc Feb 9, 2019
aab1fa7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 9, 2019
a9bf6ab
fix compile error after merge
xlc Feb 9, 2019
ca7266e
fix generic handling
xlc Feb 10, 2019
ac5a7c2
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 10, 2019
04c93be
fix comple error and bump version
xlc Feb 10, 2019
398fc3d
remove debug print
xlc Feb 10, 2019
3932cb4
fixed some tests
xlc Feb 10, 2019
6eaa5e5
update wasm blob
xlc Feb 11, 2019
fda8c0d
use u16 over u32
xlc Feb 11, 2019
9cde49d
fix build issue
xlc Feb 12, 2019
2bbdb44
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 12, 2019
c8ff235
Revert "fix build issue"
xlc Feb 12, 2019
c32a2ab
fix build error
xlc Feb 12, 2019
f0c6553
fix build error
xlc Feb 12, 2019
0153d46
fix warnings
xlc Feb 12, 2019
d6f937e
fix previous TODOs
xlc Feb 13, 2019
688f65b
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 14, 2019
1e9bd60
move to rust 2018
xlc Feb 17, 2019
3e7ee5c
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 17, 2019
cba200e
fix build issue
xlc Feb 18, 2019
382d8d3
Revert "fix build error"
xlc Feb 18, 2019
ab8c67d
workaround $crate issue
xlc Feb 19, 2019
cfd3a05
fix dependency on vec
xlc Feb 19, 2019
2b871e7
Make AccountId a new type so make it distinguishable from metadata
xlc Feb 19, 2019
dad29be
fix tests
xlc Feb 19, 2019
ea1e5d7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 19, 2019
b8fdb29
include blocks format in metadata
xlc Feb 19, 2019
e6f5e2c
fix tests
xlc Feb 21, 2019
6e8ab45
devops-parity updated wasm runtime blobs 39ade79f and merged in maste…
devops-parity Feb 21, 2019
0a8658c
support compac attribute
xlc Feb 21, 2019
618db4e
Update comments
xlc Feb 21, 2019
82a6dc7
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 25, 2019
fbcdf4b
keep original type name as display_name
xlc Feb 25, 2019
2143a7e
fix warning
xlc Feb 26, 2019
7efbcb1
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Feb 26, 2019
437244b
add module_path
xlc Feb 28, 2019
a191131
devops-parity updated wasm runtime blobs 4fc16063 and merged in maste…
devops-parity Feb 28, 2019
5a05e5d
devops-parity updated wasm runtime blobs 1b92b125 and merged in maste…
devops-parity Feb 28, 2019
a1c9219
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Mar 6, 2019
05ed4c0
fix build error and warnings
xlc Mar 6, 2019
bfe035a
Merge branch 'metadata-reflection-poc' of github.com:xlc/substrate in…
xlc Mar 6, 2019
d986bd5
fix test compiling error
xlc Mar 6, 2019
7c4c9c3
fix build issue
xlc Mar 6, 2019
121bede
revert account id change
xlc Mar 6, 2019
682b823
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Mar 7, 2019
88275c0
fix
xlc Mar 7, 2019
629eeae
Merge remote-tracking branch 'upstream/master' into metadata-reflecti…
xlc Mar 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,143 changes: 612 additions & 531 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ members = [
"node/runtime",
"node-template",
"subkey",
"core/metadata",
"core/metadata-derive",
]
exclude = [
"node/runtime/wasm",
Expand Down
22 changes: 22 additions & 0 deletions core/executor/wasm/Cargo.lock

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

17 changes: 17 additions & 0 deletions core/metadata-derive/Cargo.toml
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 = []
206 changes: 206 additions & 0 deletions core/metadata-derive/src/encode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
use quote::{quote, quote_spanned};
Copy link
Contributor

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?

Copy link
Contributor

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.


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()
}
113 changes: 113 additions & 0 deletions core/metadata-derive/src/lib.rs
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, &registry);

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
}
Loading