Skip to content

Commit

Permalink
Include bound args param in server reference information byte arg mask (
Browse files Browse the repository at this point in the history
#72588)

Considering the bound args param in the server reference information byte is required for a client-side optimization of omitting unused arguments to work properly when a server action or `"use cache"` function uses closed-over variables.

The server reference information byte is not used yet, so this PR should not change any observable behavior.
  • Loading branch information
unstubbable authored Nov 12, 2024
1 parent 25237ef commit 3898a7a
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 183 deletions.
178 changes: 79 additions & 99 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl<C: Comments> ServerActions<C> {
&self,
export_name: &str,
is_cache: bool,
params: Option<&Vec<Pat>>,
params: Option<&Vec<Param>>,
) -> String {
// Attach a checksum to the action using sha1:
// $$id = special_byte + sha1('hash_salt' + 'file_name' + ':' + 'export_name');
Expand Down Expand Up @@ -183,7 +183,7 @@ impl<C: Comments> ServerActions<C> {
// Instead, we go with the easy route and assume defined ones are
// used. This can be improved in the future.
for (i, param) in params.iter().enumerate() {
if let Pat::Rest(_) = param {
if let Pat::Rest(_) = param.pat {
// If there's a ...rest argument, we set the rest args bit
// to 1 and set the arg mask to 0b111111.
arg_mask = 0b111111;
Expand Down Expand Up @@ -322,10 +322,24 @@ impl<C: Comments> ServerActions<C> {
ids_from_closure: Vec<Name>,
arrow: &mut ArrowExpr,
) -> Box<Expr> {
let action_name = self.gen_action_ident().to_string();
let mut new_params: Vec<Param> = vec![];

if !ids_from_closure.is_empty() {
// First param is the encrypted closure variables.
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: Pat::Ident(IdentName::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into()),
});
}

for p in arrow.params.iter() {
new_params.push(Param::from(p.clone()));
}

let action_name = self.gen_action_ident().to_string();
let action_ident = Ident::new(action_name.clone().into(), arrow.span, self.private_ctxt);
let action_id = self.generate_server_reference_id(&action_name, false, Some(&arrow.params));
let action_id = self.generate_server_reference_id(&action_name, false, Some(&new_params));

self.has_action = true;
self.export_actions
Expand All @@ -348,19 +362,10 @@ impl<C: Comments> ServerActions<C> {
});
}

// export const $ACTION_myAction = async () => {}
let mut new_params: Vec<Param> = vec![];
let mut new_body: BlockStmtOrExpr = *arrow.body.clone();

if !ids_from_closure.is_empty() {
// First argument is the encrypted closure variables
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: Pat::Ident(IdentName::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into()),
});

// Also prepend the decryption decl into the body.
// Prepend the decryption declaration to the body.
// var [arg1, arg2, arg3] = await decryptActionBoundArgs(actionId,
// $$ACTION_CLOSURE_BOUND)
let decryption_decl = VarDecl {
Expand Down Expand Up @@ -407,14 +412,6 @@ impl<C: Comments> ServerActions<C> {
}
}

for p in arrow.params.iter() {
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: p.clone(),
});
}

// Create the action export decl from the arrow function
// export const $$RSC_SERVER_ACTION_0 = async function action($$ACTION_CLOSURE_BOUND) {}
self.hoisted_extra_items
Expand Down Expand Up @@ -465,14 +462,22 @@ impl<C: Comments> ServerActions<C> {
function: &mut Box<Function>,
fn_name: Option<Ident>,
) -> Box<Expr> {
let action_name: JsWord = self.gen_action_ident();
let mut new_params: Vec<Param> = vec![];

if !ids_from_closure.is_empty() {
// First param is the encrypted closure variables.
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: Pat::Ident(IdentName::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into()),
});
}

new_params.append(&mut function.params);

let action_name: JsWord = self.gen_action_ident();
let action_ident = Ident::new(action_name.clone(), function.span, self.private_ctxt);
let action_id = self.generate_server_reference_id(
&action_name,
false,
Some(&function.params.iter().map(|p| p.pat.clone()).collect()),
);
let action_id = self.generate_server_reference_id(&action_name, false, Some(&new_params));

self.has_action = true;
self.export_actions
Expand All @@ -497,20 +502,10 @@ impl<C: Comments> ServerActions<C> {
private_ctxt: self.private_ctxt,
});

// export async function $ACTION_myAction () {}
let mut new_params: Vec<Param> = vec![];
let mut new_body: Option<BlockStmt> = function.body.clone();

// add params from closure collected ids
if !ids_from_closure.is_empty() {
// First argument is the encrypted closure variables
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: Pat::Ident(IdentName::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into()),
});

// Also prepend the decryption decl into the body.
// Prepend the decryption declaration to the body.
// var [arg1, arg2, arg3] = await decryptActionBoundArgs(actionId,
// $$ACTION_CLOSURE_BOUND)
let decryption_decl = VarDecl {
Expand Down Expand Up @@ -547,10 +542,6 @@ impl<C: Comments> ServerActions<C> {
}
}

for p in function.params.iter() {
new_params.push(p.clone());
}

// Create the action export decl from the function
// export const $$RSC_SERVER_ACTION_0 = async function action($$ACTION_CLOSURE_BOUND) {}
self.hoisted_extra_items
Expand Down Expand Up @@ -587,25 +578,6 @@ impl<C: Comments> ServerActions<C> {
cache_type: &str,
arrow: &mut ArrowExpr,
) -> Box<Expr> {
let cache_name: JsWord = self.gen_cache_ident();
let cache_ident = private_ident!(cache_name.clone());
let export_name: JsWord = cache_name;

let reference_id =
self.generate_server_reference_id(&export_name, true, Some(&arrow.params));

self.has_cache = true;
self.has_action = true;
self.export_actions
.push((export_name.to_string(), reference_id.clone()));

if let BlockStmtOrExpr::BlockStmt(block) = &mut *arrow.body {
block.visit_mut_with(&mut ClosureReplacer {
used_ids: &ids_from_closure,
private_ctxt: self.private_ctxt,
});
}

let mut new_params: Vec<Param> = vec![];

// Add the collected closure variables as the first parameter to the
Expand All @@ -620,10 +592,24 @@ impl<C: Comments> ServerActions<C> {
}

for p in arrow.params.iter() {
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: p.clone(),
new_params.push(Param::from(p.clone()));
}

let cache_name: JsWord = self.gen_cache_ident();
let cache_ident = private_ident!(cache_name.clone());
let export_name: JsWord = cache_name;

let reference_id = self.generate_server_reference_id(&export_name, true, Some(&new_params));

self.has_cache = true;
self.has_action = true;
self.export_actions
.push((export_name.to_string(), reference_id.clone()));

if let BlockStmtOrExpr::BlockStmt(block) = &mut *arrow.body {
block.visit_mut_with(&mut ClosureReplacer {
used_ids: &ids_from_closure,
private_ctxt: self.private_ctxt,
});
}

Expand Down Expand Up @@ -727,14 +713,27 @@ impl<C: Comments> ServerActions<C> {
cache_type: &str,
function: &mut Box<Function>,
) -> Box<Expr> {
let mut new_params: Vec<Param> = vec![];

// Add the collected closure variables as the first parameter to the
// function. They are unencrypted and passed into this function by the
// cache wrapper.
if !ids_from_closure.is_empty() {
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: self.create_bound_action_args_array_pat(ids_from_closure.len()),
});
}

for p in function.params.iter() {
new_params.push(p.clone());
}

let cache_name: JsWord = self.gen_cache_ident();
let cache_ident = private_ident!(cache_name.clone());

let reference_id = self.generate_server_reference_id(
&cache_name,
true,
Some(&function.params.iter().map(|p| p.pat.clone()).collect()),
);
let reference_id = self.generate_server_reference_id(&cache_name, true, Some(&new_params));

self.has_cache = true;
self.has_action = true;
Expand All @@ -752,23 +751,6 @@ impl<C: Comments> ServerActions<C> {
private_ctxt: self.private_ctxt,
});

let mut new_params: Vec<Param> = vec![];

// Add the collected closure variables as the first parameter to the
// function. They are unencrypted and passed into this function by the
// cache wrapper.
if !ids_from_closure.is_empty() {
new_params.push(Param {
span: DUMMY_SP,
decorators: vec![],
pat: self.create_bound_action_args_array_pat(ids_from_closure.len()),
});
}

for p in function.params.iter() {
new_params.push(p.clone());
}

// export var cache_ident = async function() {}
self.hoisted_extra_items
.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
Expand Down Expand Up @@ -1403,13 +1385,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
self.generate_server_reference_id(
f.ident.sym.as_ref(),
ref_id,
Some(
&f.function
.params
.iter()
.map(|p| p.pat.clone())
.collect(),
),
Some(&f.function.params),
),
));
}
Expand Down Expand Up @@ -1525,9 +1501,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
let ref_id = self.generate_server_reference_id(
"default",
is_cache,
Some(
&f.function.params.iter().map(|p| p.pat.clone()).collect(),
),
Some(&f.function.params),
);

if let Some(ident) = &f.ident {
Expand Down Expand Up @@ -1611,7 +1585,13 @@ impl<C: Comments> VisitMut for ServerActions<C> {
self.generate_server_reference_id(
"default",
is_cache,
Some(&arrow.params),
Some(
&arrow
.params
.iter()
.map(|p| Param::from(p.clone()))
.collect(),
),
),
));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/* __next_internal_action_entry_do_not_use__ {"006a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","0090b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"406a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","4090b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import deleteFromDb from 'db';
export const $$RSC_SERVER_ACTION_0 = async function deleteItem($$ACTION_CLOSURE_BOUND) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
await deleteFromDb($$ACTION_ARG_0);
await deleteFromDb($$ACTION_ARG_1);
};
export function Item({ id1, id2 }) {
var deleteItem = registerServerReference($$RSC_SERVER_ACTION_0, "006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
var deleteItem = registerServerReference($$RSC_SERVER_ACTION_0, "406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
id1,
id2
]));
return <Button action={deleteItem}>Delete</Button>;
}
export const $$RSC_SERVER_ACTION_1 = async function action($$ACTION_CLOSURE_BOUND) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("0090b5db271335765a4b0eab01f044b381b5ebd5cd", $$ACTION_CLOSURE_BOUND);
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("4090b5db271335765a4b0eab01f044b381b5ebd5cd", $$ACTION_CLOSURE_BOUND);
console.log($$ACTION_ARG_0);
console.log($$ACTION_ARG_1);
};
Expand All @@ -23,7 +23,7 @@ export default function Home() {
name: 'John',
test: 'test'
};
const action = registerServerReference($$RSC_SERVER_ACTION_1, "0090b5db271335765a4b0eab01f044b381b5ebd5cd", null).bind(null, encryptActionBoundArgs("0090b5db271335765a4b0eab01f044b381b5ebd5cd", [
const action = registerServerReference($$RSC_SERVER_ACTION_1, "4090b5db271335765a4b0eab01f044b381b5ebd5cd", null).bind(null, encryptActionBoundArgs("4090b5db271335765a4b0eab01f044b381b5ebd5cd", [
info.name,
info.test
]));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/* __next_internal_action_entry_do_not_use__ {"006a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91":"$$RSC_SERVER_ACTION_2","7f90b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"406a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91":"$$RSC_SERVER_ACTION_2","7f90b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import deleteFromDb from 'db';
const v1 = 'v1';
export const $$RSC_SERVER_ACTION_0 = async function deleteItem($$ACTION_CLOSURE_BOUND) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
await deleteFromDb($$ACTION_ARG_0);
await deleteFromDb(v1);
await deleteFromDb($$ACTION_ARG_1);
};
export function Item({ id1, id2 }) {
const v2 = id2;
const deleteItem = registerServerReference($$RSC_SERVER_ACTION_0, "006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
const deleteItem = registerServerReference($$RSC_SERVER_ACTION_0, "406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
id1,
v2
]));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
/* __next_internal_action_entry_do_not_use__ {"006a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","0090b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
/* __next_internal_action_entry_do_not_use__ {"406a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","4090b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import deleteFromDb from 'db';
const v1 = 'v1';
export const $$RSC_SERVER_ACTION_0 = async function action($$ACTION_CLOSURE_BOUND) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);
await deleteFromDb($$ACTION_ARG_0);
await deleteFromDb(v1);
await deleteFromDb($$ACTION_ARG_1);
};
export const $$RSC_SERVER_ACTION_1 = async function action($$ACTION_CLOSURE_BOUND) {
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("0090b5db271335765a4b0eab01f044b381b5ebd5cd", $$ACTION_CLOSURE_BOUND);
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("4090b5db271335765a4b0eab01f044b381b5ebd5cd", $$ACTION_CLOSURE_BOUND);
await deleteFromDb($$ACTION_ARG_0);
await deleteFromDb(v1);
await deleteFromDb($$ACTION_ARG_1);
};
export function Item({ id1, id2 }) {
const v2 = id2;
return <>
<Button action={registerServerReference($$RSC_SERVER_ACTION_0, "006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("006a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
<Button action={registerServerReference($$RSC_SERVER_ACTION_0, "406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null).bind(null, encryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", [
id1,
v2
]))}>
Delete
</Button>
<Button action={registerServerReference($$RSC_SERVER_ACTION_1, "0090b5db271335765a4b0eab01f044b381b5ebd5cd", null).bind(null, encryptActionBoundArgs("0090b5db271335765a4b0eab01f044b381b5ebd5cd", [
<Button action={registerServerReference($$RSC_SERVER_ACTION_1, "4090b5db271335765a4b0eab01f044b381b5ebd5cd", null).bind(null, encryptActionBoundArgs("4090b5db271335765a4b0eab01f044b381b5ebd5cd", [
id1,
v2
]))}>
Expand Down
Loading

0 comments on commit 3898a7a

Please sign in to comment.