lang: Handle arrays with const as length (#968)

This commit is contained in:
Tom Linton 2021-12-23 17:13:46 +13:00 committed by GitHub
parent f4fe7d4440
commit 51aeb08ae1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 1 deletions

View File

@ -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

View File

@ -402,9 +402,14 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result<Vec<IdlTypeDefinition>> {
.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::<Result<Vec<IdlField>>>(),
@ -455,6 +460,33 @@ fn parse_ty_defs(ctx: &CrateContext) -> Result<Vec<IdlTypeDefinition>> {
.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);

View File

@ -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],
}

View File

@ -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>,
}

View File

@ -87,6 +87,11 @@ pub mod misc {
Ok(())
}
pub fn test_const_array_size(ctx: Context<TestConstArraySize>, data: u8) -> ProgramResult {
ctx.accounts.data.data[0] = data;
Ok(())
}
pub fn test_close(_ctx: Context<TestClose>) -> ProgramResult {
Ok(())
}

View File

@ -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(