mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 16:57:58 +00:00
bc53b9a03a
* Change copyright year to 2023 from 2022 * Fix incorrect update of copyright year * Remove years from copy right header * Fix remaining files * Fix typo in a header and remove update-copyright.sh
263 lines
7.7 KiB
Rust
263 lines
7.7 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use codec::Encode;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::quote;
|
|
use syn::{
|
|
parse::{Error, Result},
|
|
parse_macro_input,
|
|
spanned::Spanned as _,
|
|
Expr, ExprLit, FieldValue, ItemConst, Lit,
|
|
};
|
|
|
|
/// This macro accepts a `const` item that has a struct initializer expression of
|
|
/// `RuntimeVersion`-like type. The macro will pass through this declaration and append an item
|
|
/// declaration that will lead to emitting a wasm custom section with the contents of
|
|
/// `RuntimeVersion`.
|
|
pub fn decl_runtime_version_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
let item = parse_macro_input!(input as ItemConst);
|
|
decl_runtime_version_impl_inner(item)
|
|
.unwrap_or_else(|e| e.to_compile_error())
|
|
.into()
|
|
}
|
|
|
|
fn decl_runtime_version_impl_inner(item: ItemConst) -> Result<TokenStream> {
|
|
let runtime_version = ParseRuntimeVersion::parse_expr(&item.expr)?.build(item.expr.span())?;
|
|
let link_section =
|
|
generate_emit_link_section_decl(&runtime_version.encode(), "runtime_version");
|
|
|
|
Ok(quote! {
|
|
#item
|
|
#link_section
|
|
})
|
|
}
|
|
|
|
/// This is a duplicate of `sp_version::RuntimeVersion`. We cannot unfortunately use the original
|
|
/// declaration, because if we directly depend on `sp_version` from this proc-macro cargo will
|
|
/// enable `std` feature even for `no_std` wasm runtime builds.
|
|
///
|
|
/// One difference from the original definition is the `apis` field. Since we don't actually parse
|
|
/// `apis` from this macro it will always be emitteed as empty. An empty vector can be encoded as
|
|
/// a zero-byte, thus `u8` is sufficient here.
|
|
#[derive(Encode)]
|
|
struct RuntimeVersion {
|
|
spec_name: String,
|
|
impl_name: String,
|
|
authoring_version: u32,
|
|
spec_version: u32,
|
|
impl_version: u32,
|
|
apis: u8,
|
|
transaction_version: u32,
|
|
state_version: u8,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
struct ParseRuntimeVersion {
|
|
spec_name: Option<String>,
|
|
impl_name: Option<String>,
|
|
authoring_version: Option<u32>,
|
|
spec_version: Option<u32>,
|
|
impl_version: Option<u32>,
|
|
transaction_version: Option<u32>,
|
|
state_version: Option<u8>,
|
|
}
|
|
|
|
impl ParseRuntimeVersion {
|
|
fn parse_expr(init_expr: &Expr) -> Result<ParseRuntimeVersion> {
|
|
let init_expr = match init_expr {
|
|
Expr::Struct(ref e) => e,
|
|
_ =>
|
|
return Err(Error::new(init_expr.span(), "expected a struct initializer expression")),
|
|
};
|
|
|
|
let mut parsed = ParseRuntimeVersion::default();
|
|
for field_value in init_expr.fields.iter() {
|
|
parsed.parse_field_value(field_value)?;
|
|
}
|
|
Ok(parsed)
|
|
}
|
|
|
|
fn parse_field_value(&mut self, field_value: &FieldValue) -> Result<()> {
|
|
let field_name = match field_value.member {
|
|
syn::Member::Named(ref ident) => ident,
|
|
syn::Member::Unnamed(_) =>
|
|
return Err(Error::new(field_value.span(), "only named members must be used")),
|
|
};
|
|
|
|
fn parse_once<T>(
|
|
value: &mut Option<T>,
|
|
field: &FieldValue,
|
|
parser: impl FnOnce(&Expr) -> Result<T>,
|
|
) -> Result<()> {
|
|
if value.is_some() {
|
|
Err(Error::new(field.span(), "field is already initialized before"))
|
|
} else {
|
|
*value = Some(parser(&field.expr)?);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
if field_name == "spec_name" {
|
|
parse_once(&mut self.spec_name, field_value, Self::parse_str_literal)?;
|
|
} else if field_name == "impl_name" {
|
|
parse_once(&mut self.impl_name, field_value, Self::parse_str_literal)?;
|
|
} else if field_name == "authoring_version" {
|
|
parse_once(&mut self.authoring_version, field_value, Self::parse_num_literal)?;
|
|
} else if field_name == "spec_version" {
|
|
parse_once(&mut self.spec_version, field_value, Self::parse_num_literal)?;
|
|
} else if field_name == "impl_version" {
|
|
parse_once(&mut self.impl_version, field_value, Self::parse_num_literal)?;
|
|
} else if field_name == "transaction_version" {
|
|
parse_once(&mut self.transaction_version, field_value, Self::parse_num_literal)?;
|
|
} else if field_name == "state_version" {
|
|
parse_once(&mut self.state_version, field_value, Self::parse_num_literal_u8)?;
|
|
} else if field_name == "apis" {
|
|
// Intentionally ignored
|
|
//
|
|
// The definition will pass through for the declaration, however, it won't get into
|
|
// the "runtime_version" custom section. `impl_runtime_apis` is responsible for
|
|
// generating a custom section with the supported runtime apis descriptor.
|
|
} else {
|
|
return Err(Error::new(field_name.span(), "unknown field"))
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_num_literal(expr: &Expr) -> Result<u32> {
|
|
let lit = match *expr {
|
|
Expr::Lit(ExprLit { lit: Lit::Int(ref lit), .. }) => lit,
|
|
_ =>
|
|
return Err(Error::new(
|
|
expr.span(),
|
|
"only numeric literals (e.g. `10`) are supported here",
|
|
)),
|
|
};
|
|
lit.base10_parse::<u32>()
|
|
}
|
|
|
|
fn parse_num_literal_u8(expr: &Expr) -> Result<u8> {
|
|
let lit = match *expr {
|
|
Expr::Lit(ExprLit { lit: Lit::Int(ref lit), .. }) => lit,
|
|
_ =>
|
|
return Err(Error::new(
|
|
expr.span(),
|
|
"only numeric literals (e.g. `10`) are supported here",
|
|
)),
|
|
};
|
|
lit.base10_parse::<u8>()
|
|
}
|
|
|
|
fn parse_str_literal(expr: &Expr) -> Result<String> {
|
|
let mac = match *expr {
|
|
Expr::Macro(syn::ExprMacro { ref mac, .. }) => mac,
|
|
_ => return Err(Error::new(expr.span(), "a macro expression is expected here")),
|
|
};
|
|
|
|
let lit: ExprLit = mac.parse_body().map_err(|e| {
|
|
Error::new(
|
|
e.span(),
|
|
format!("a single literal argument is expected, but parsing is failed: {}", e),
|
|
)
|
|
})?;
|
|
|
|
match lit.lit {
|
|
Lit::Str(ref lit) => Ok(lit.value()),
|
|
_ => Err(Error::new(lit.span(), "only string literals are supported here")),
|
|
}
|
|
}
|
|
|
|
fn build(self, span: Span) -> Result<RuntimeVersion> {
|
|
macro_rules! required {
|
|
($e:expr) => {
|
|
$e.ok_or_else(|| {
|
|
Error::new(span, format!("required field '{}' is missing", stringify!($e)))
|
|
})?
|
|
};
|
|
}
|
|
|
|
let Self {
|
|
spec_name,
|
|
impl_name,
|
|
authoring_version,
|
|
spec_version,
|
|
impl_version,
|
|
transaction_version,
|
|
state_version,
|
|
} = self;
|
|
|
|
Ok(RuntimeVersion {
|
|
spec_name: required!(spec_name),
|
|
impl_name: required!(impl_name),
|
|
authoring_version: required!(authoring_version),
|
|
spec_version: required!(spec_version),
|
|
impl_version: required!(impl_version),
|
|
transaction_version: required!(transaction_version),
|
|
state_version: required!(state_version),
|
|
apis: 0,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn generate_emit_link_section_decl(contents: &[u8], section_name: &str) -> TokenStream {
|
|
let len = contents.len();
|
|
quote! {
|
|
const _: () = {
|
|
#[cfg(not(feature = "std"))]
|
|
#[link_section = #section_name]
|
|
static SECTION_CONTENTS: [u8; #len] = [#(#contents),*];
|
|
};
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::borrow::Cow;
|
|
|
|
#[test]
|
|
fn version_can_be_deserialized() {
|
|
let version_bytes = RuntimeVersion {
|
|
spec_name: "hello".to_string(),
|
|
impl_name: "world".to_string(),
|
|
authoring_version: 10,
|
|
spec_version: 265,
|
|
impl_version: 1,
|
|
apis: 0,
|
|
transaction_version: 2,
|
|
state_version: 1,
|
|
}
|
|
.encode();
|
|
|
|
assert_eq!(
|
|
sp_version::RuntimeVersion::decode_with_version_hint(&mut &version_bytes[..], Some(4))
|
|
.unwrap(),
|
|
sp_version::RuntimeVersion {
|
|
spec_name: "hello".into(),
|
|
impl_name: "world".into(),
|
|
authoring_version: 10,
|
|
spec_version: 265,
|
|
impl_version: 1,
|
|
apis: Cow::Owned(vec![]),
|
|
transaction_version: 2,
|
|
state_version: 1,
|
|
},
|
|
);
|
|
}
|
|
}
|