Skip to content

Commit

Permalink
Forbid super in static class methods with server function directives
Browse files Browse the repository at this point in the history
Static class methods that are annotated with `"use cache"` or `"use
server"` must not call `super`.
  • Loading branch information
unstubbable committed Nov 22, 2024
1 parent 0b4468a commit dcbd0da
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
39 changes: 39 additions & 0 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2158,6 +2158,16 @@ impl<C: Comments> VisitMut for ServerActions<C> {
}
}

fn visit_mut_super(&mut self, n: &mut Super) {
if let ThisStatus::Forbidden { directive } = &self.this_status {
emit_error(ServerActionsErrorKind::ForbiddenExpression {
span: n.span,
expr: "super".into(),
directive: directive.clone(),
});
}
}

fn visit_mut_ident(&mut self, n: &mut Ident) {
if n.sym == *"arguments" {
if let ThisStatus::Forbidden { directive } = &self.this_status {
Expand Down Expand Up @@ -2758,6 +2768,35 @@ impl ClosureReplacer<'_> {
}

impl VisitMut for ClosureReplacer<'_> {
fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
stmt.visit_mut_children_with(self);

// Replace super calls in hoisted static class methods with empty
// statements so that we can remove them later to ensure that the
// resulting JS is still valid. Before this, we have already emitted an
// error for the super call.
if let Stmt::Expr(ExprStmt {
expr:
box Expr::Call(CallExpr {
callee: Callee::Expr(box Expr::SuperProp(SuperPropExpr { .. })),
..
}),
..
}) = stmt
{
stmt.take();
}
}

fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
stmts.visit_mut_children_with(self);

stmts.retain(|stmt| {
// Remove empty statements.
!matches!(stmt, Stmt::Empty(..))
});
}

fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
export class MyClass {
import { BaseClass } from './base'

export class MyClass extends BaseClass {
static async foo() {
// super is allowed here
super.foo()

return fetch('https://example.com').then((res) => res.json())
}
static async bar() {
'use cache'

// super is not allowed here
super.bar()
// arguments is not allowed here
console.log(arguments)
// this is not allowed here
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* __next_internal_action_entry_do_not_use__ {"803128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0"} */ import { registerServerReference } from "private-next-rsc-server-reference";
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
import { cache as $$cache__ } from "private-next-rsc-cache-wrapper";
import { BaseClass } from './base';
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "803128060c414d59f8552e4788b846c0d2b7f74743", 0, /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ async function bar() {
// arguments is not allowed here
console.log(arguments);
Expand All @@ -11,8 +12,10 @@ Object.defineProperty($$RSC_SERVER_CACHE_0, "name", {
"value": "bar",
"writable": false
});
export class MyClass {
export class MyClass extends BaseClass {
static async foo() {
// super is allowed here
super.foo();
return fetch('https://example.com').then((res)=>res.json());
}
static bar = registerServerReference($$RSC_SERVER_CACHE_0, "803128060c414d59f8552e4788b846c0d2b7f74743", null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
x "use cache" functions can not use `super`.
|
,-[input.js:14:1]
13 | // super is not allowed here
14 | super.bar()
: ^^^^^
15 | // arguments is not allowed here
`----
x "use cache" functions can not use `arguments`.
|
,-[input.js:9:1]
8 | // arguments is not allowed here
9 | console.log(arguments)
,-[input.js:16:1]
15 | // arguments is not allowed here
16 | console.log(arguments)
: ^^^^^^^^^
10 | // this is not allowed here
17 | // this is not allowed here
`----
x "use cache" functions can not use `this`.
|
,-[input.js:11:1]
10 | // this is not allowed here
11 | return this.foo()
,-[input.js:18:1]
17 | // this is not allowed here
18 | return this.foo()
: ^^^^
12 | }
19 | }
`----

0 comments on commit dcbd0da

Please sign in to comment.