From 51aeb08ae1c93f9f759c6e244e3fa724c9a916a7 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 23 Dec 2021 17:13:46 +1300 Subject: [PATCH] lang: Handle arrays with const as length (#968) --- CHANGELOG.md | 1 + lang/syn/src/idl/file.rs | 34 ++++++++++++++++++++++++- tests/misc/programs/misc/src/account.rs | 7 +++++ tests/misc/programs/misc/src/context.rs | 6 +++++ tests/misc/programs/misc/src/lib.rs | 5 ++++ tests/misc/tests/misc.js | 18 +++++++++++++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f32ac1e3..908f27937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ incremented for features. * lang,ts,ci,cli,docs: update solana toolchain to version 1.8.5([#1133](https://github.com/project-serum/anchor/pull/1133)) * ts: Add optional commitment argument to `fetch` and `fetchMultiple` ([#1171](https://github.com/project-serum/anchor/pull/1171)) * lang: Add `set_inner` method to `Account<'a, T>` to enable easy updates ([#1177](https://github.com/project-serum/anchor/pull/1177)) +* lang: Handle arrays with const as length ([#968](https://github.com/project-serum/anchor/pull/968)). ### Breaking diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index ca90525a8..9aa1f386e 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -402,9 +402,14 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result> { .map(|f: &syn::Field| { let mut tts = proc_macro2::TokenStream::new(); f.ty.to_tokens(&mut tts); + // Handle array sizes that are constants + let mut tts_string = tts.to_string(); + if tts_string.starts_with('[') { + tts_string = resolve_variable_array_length(ctx, tts_string); + } Ok(IdlField { name: f.ident.as_ref().unwrap().to_string().to_mixed_case(), - ty: tts.to_string().parse()?, + ty: tts_string.parse()?, }) }) .collect::>>(), @@ -455,6 +460,33 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result> { .collect() } +// Replace variable array lengths with values +fn resolve_variable_array_length(ctx: &CrateContext, tts_string: String) -> String { + for constant in ctx.consts() { + if constant.ty.to_token_stream().to_string() == "usize" + && tts_string.contains(&constant.ident.to_string()) + { + // Check for the existence of consts existing elsewhere in the + // crate which have the same name, are usize, and have a + // different value. We can't know which was intended for the + // array size from ctx. + if ctx.consts().any(|c| { + c != constant + && c.ident == constant.ident + && c.ty == constant.ty + && c.expr != constant.expr + }) { + panic!("Crate wide unique name required for array size const."); + } + return tts_string.replace( + &constant.ident.to_string(), + &constant.expr.to_token_stream().to_string(), + ); + } + } + tts_string +} + fn to_idl_type(f: &syn::Field) -> IdlType { let mut tts = proc_macro2::TokenStream::new(); f.ty.to_tokens(&mut tts); diff --git a/tests/misc/programs/misc/src/account.rs b/tests/misc/programs/misc/src/account.rs index 1f4cfdf88..b94e1fdcf 100644 --- a/tests/misc/programs/misc/src/account.rs +++ b/tests/misc/programs/misc/src/account.rs @@ -1,5 +1,7 @@ use anchor_lang::prelude::*; +pub const MAX_SIZE: usize = 10; + #[account] pub struct Data { pub udata: u128, @@ -41,3 +43,8 @@ pub struct DataWithFilter { pub struct DataMultidimensionalArray { pub data: [[u8; 10]; 10], } + +#[account] +pub struct DataConstArraySize { + pub data: [u8; MAX_SIZE], +} diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 6caa1e9ab..f0a34b431 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -330,3 +330,9 @@ pub struct TestMultidimensionalArray<'info> { #[account(zero)] pub data: Account<'info, DataMultidimensionalArray>, } + +#[derive(Accounts)] +pub struct TestConstArraySize<'info> { + #[account(zero)] + pub data: Account<'info, DataConstArraySize>, +} diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index 6a49ae1f6..8a77ccb4b 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -87,6 +87,11 @@ pub mod misc { Ok(()) } + pub fn test_const_array_size(ctx: Context, data: u8) -> ProgramResult { + ctx.accounts.data.data[0] = data; + Ok(()) + } + pub fn test_close(_ctx: Context) -> ProgramResult { Ok(()) } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index eae67a48c..3cd405a91 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -836,6 +836,24 @@ describe("misc", () => { assert.ok(account.data, 3); }); + it("Can use const for array size", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testConstArraySize(99, { + accounts: { + data: data.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); + }); + it("Should include BASE const in IDL", async () => { assert( miscIdl.constants.find(