Skip to content

Commit

Permalink
lang: codegen improvements (#1766)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-schaaf authored Apr 12, 2022
1 parent b28deb3 commit 6bd9879
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 47 deletions.
22 changes: 13 additions & 9 deletions lang/syn/src/codegen/accounts/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
let field = &f.ident;
let name_str = field.to_string();
let ty_decl = f.ty_decl();
let from_account_info = f.from_account_info_unchecked(None);
let from_account_info = f.from_account_info(None, false);
quote! {
let #field: #ty_decl = {
let mut __data: &[u8] = &#field.try_borrow_data()?;
Expand Down Expand Up @@ -340,7 +340,8 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
};

// Convert from account info to account context wrapper type.
let from_account_info = f.from_account_info_unchecked(Some(&c.kind));
let from_account_info = f.from_account_info(Some(&c.kind), true);
let from_account_info_unchecked = f.from_account_info(Some(&c.kind), false);

// PDA bump seeds.
let (find_pda, seeds_with_bump) = match &c.seeds {
Expand Down Expand Up @@ -408,7 +409,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
anchor_spl::token::initialize_account(cpi_ctx)?;
}

let pa: #ty_decl = #from_account_info;
let pa: #ty_decl = #from_account_info_unchecked;
if #if_needed {
if pa.mint != #mint.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
Expand Down Expand Up @@ -443,7 +444,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts);
anchor_spl::associated_token::create(cpi_ctx)?;
}
let pa: #ty_decl = #from_account_info;
let pa: #ty_decl = #from_account_info_unchecked;
if #if_needed {
if pa.mint != #mint.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key())));
Expand Down Expand Up @@ -496,7 +497,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
}
let pa: #ty_decl = #from_account_info;
let pa: #ty_decl = #from_account_info_unchecked;
if #if_needed {
if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority).with_account_name(#name_str));
Expand Down Expand Up @@ -548,16 +549,19 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma

// Create the account. Always do this in the event
// if needed is not specified or the system program is the owner.
if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
let pa: #ty_decl = if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
// Define the payer variable.
#payer

// CPI to the system program to create.
#create_account
}

// Convert from account info to account context wrapper type.
let pa: #ty_decl = #from_account_info;
// Convert from account info to account context wrapper type.
#from_account_info_unchecked
} else {
// Convert from account info to account context wrapper type.
#from_account_info
};

// Assert the account was created correctly.
if #if_needed {
Expand Down
88 changes: 66 additions & 22 deletions lang/syn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,51 +281,95 @@ impl Field {

// TODO: remove the option once `CpiAccount` is completely removed (not
// just deprecated).
pub fn from_account_info_unchecked(&self, kind: Option<&InitKind>) -> proc_macro2::TokenStream {
pub fn from_account_info(
&self,
kind: Option<&InitKind>,
checked: bool,
) -> proc_macro2::TokenStream {
let field = &self.ident;
let container_ty = self.container_ty();
let owner_addr = match &kind {
None => quote! { program_id },
Some(InitKind::Program { .. }) => quote! {
program_id
},
_ => quote! {
&anchor_spl::token::ID
},
};
match &self.ty {
Ty::AccountInfo => quote! { #field.to_account_info() },
Ty::UncheckedAccount => {
quote! { UncheckedAccount::try_from(#field.to_account_info()) }
}
Ty::Account(AccountTy { boxed, .. }) => {
if *boxed {
let stream = if checked {
quote! {
Box::new(#container_ty::try_from_unchecked(
#container_ty::try_from(
&#field,
)?)
)?
}
} else {
quote! {
#container_ty::try_from_unchecked(
&#field,
)?
}
};
if *boxed {
quote! {
Box::new(#stream)
}
} else {
stream
}
}
Ty::CpiAccount(_) => {
quote! {
#container_ty::try_from_unchecked(
&#field,
)?
if checked {
quote! {
#container_ty::try_from(
&#field,
)?
}
} else {
quote! {
#container_ty::try_from_unchecked(
&#field,
)?
}
}
}
Ty::AccountLoader(_) => {
if checked {
quote! {
#container_ty::try_from(
&#field,
)?
}
} else {
quote! {
#container_ty::try_from_unchecked(
#owner_addr,
&#field,
)?
}
}
}
_ => {
let owner_addr = match &kind {
None => quote! { program_id },
Some(InitKind::Program { .. }) => quote! {
program_id
},
_ => quote! {
&anchor_spl::token::ID
},
};
quote! {
#container_ty::try_from_unchecked(
#owner_addr,
&#field,
)?
if checked {
quote! {
#container_ty::try_from(
#owner_addr,
&#field,
)?
}
} else {
quote! {
#container_ty::try_from_unchecked(
#owner_addr,
&#field,
)?
}
}
}
}
Expand Down
8 changes: 1 addition & 7 deletions tests/misc/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ wallet = "~/.config/solana/id.json"
[programs.localnet]
misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"
misc2 = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"

[[test.genesis]]
address = "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr"
program = "./target/deploy/misc.so"
init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2"

[workspace]
exclude = ["programs/shared"]

[scripts]
test = "yarn run ts-mocha -t 1000000 tests/*.ts"
4 changes: 2 additions & 2 deletions tests/misc/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
# a validator with a version < 1.9, so it can test
# whether anchor's rent-exemption checks work for
# legacy accounts which dont have to be rent-exempt
rm ./tests/misc.ts
mv miscNonRentExempt.ts ./tests/miscNonRentExempt.ts
rm ./tests/misc/misc.ts
mv miscNonRentExempt.ts ./tests/misc/miscNonRentExempt.ts
2 changes: 1 addition & 1 deletion tests/misc/miscNonRentExempt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SystemProgram,
SYSVAR_RENT_PUBKEY,
} from "@solana/web3.js";
import { Misc } from "../target/types/misc";
import { Misc } from "../../target/types/misc";
const { assert } = require("chai");

describe("miscNonRentExempt", () => {
Expand Down
19 changes: 19 additions & 0 deletions tests/misc/programs/init-if-needed/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "init-if-needed"
version = "0.1.0"
description = "Created with Anchor"
edition = "2018"

[lib]
crate-type = ["cdylib", "lib"]
name = "init_if_needed"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
2 changes: 2 additions & 0 deletions tests/misc/programs/init-if-needed/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
61 changes: 61 additions & 0 deletions tests/misc/programs/init-if-needed/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use anchor_lang::prelude::*;

declare_id!("BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2");

#[program]
pub mod init_if_needed {
use super::*;

// _val only used to make tx different so that it doesn't result
// in dup tx error
pub fn initialize(ctx: Context<Initialize>, _val: u8) -> Result<()> {
ctx.accounts.acc.val = 1000;
Ok(())
}

pub fn second_initialize(ctx: Context<SecondInitialize>, _val: u8) -> Result<()> {
ctx.accounts.acc.other_val = 2000;
Ok(())
}

pub fn close(ctx: Context<Close>) -> Result<()> {
ctx.accounts.acc.val = 5000;
Ok(())
}
}

#[account]
pub struct MyData {
pub val: u64,
}

#[account]
pub struct OtherData {
pub other_val: u64,
}

#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init_if_needed, payer = payer, space = 8 + 8)]
pub acc: Account<'info, MyData>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct SecondInitialize<'info> {
#[account(init, payer = payer, space = 8 + 8)]
pub acc: Account<'info, OtherData>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Close<'info> {
#[account(mut, close = receiver)]
pub acc: Account<'info, MyData>,
#[account(mut)]
pub receiver: UncheckedAccount<'info>,
}
2 changes: 2 additions & 0 deletions tests/misc/tests/init-if-needed/Test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[scripts]
test = "yarn run ts-mocha -t 1000000 ./tests/init-if-needed/*.ts"
Loading

0 comments on commit 6bd9879

Please sign in to comment.