ソースを参照

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

Tom Linton 3 年 前
コミット
51aeb08ae1

+ 1 - 0
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
 

+ 33 - 1
lang/syn/src/idl/file.rs

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

+ 7 - 0
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],
+}

+ 6 - 0
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>,
+}

+ 5 - 0
tests/misc/programs/misc/src/lib.rs

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

+ 18 - 0
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(