mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Validate destructuring assignment to globals
Fixes one more category of bug. For assignment expressions, we validating against redeclaring a global variable when the assignment target was an identifier, but not when the global was reassigned via destructuring. This PR adds a `lowerIdentifierForAssignment()` helper and uses it for assignment of all identifier variants, including destructuring.
This commit is contained in:
@@ -2439,6 +2439,41 @@ function getLoadKind(
|
||||
return isContext ? "LoadContext" : "LoadLocal";
|
||||
}
|
||||
|
||||
function lowerIdentifierForAssignment(
|
||||
builder: HIRBuilder,
|
||||
loc: SourceLocation,
|
||||
kind: InstructionKind,
|
||||
path: NodePath<t.Identifier>
|
||||
): Place | null {
|
||||
const identifier = builder.resolveIdentifier(path);
|
||||
if (identifier == null) {
|
||||
if (kind === InstructionKind.Reassign) {
|
||||
// Trying to reassign a global is not allowed
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
nodePath: path,
|
||||
});
|
||||
} else {
|
||||
// Else its an internal error bc we couldn't find the binding
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
nodePath: path,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const place: Place = {
|
||||
kind: "Identifier",
|
||||
identifier: identifier,
|
||||
effect: Effect.Unknown,
|
||||
loc,
|
||||
};
|
||||
return place;
|
||||
}
|
||||
|
||||
function lowerAssignment(
|
||||
builder: HIRBuilder,
|
||||
loc: SourceLocation,
|
||||
@@ -2450,23 +2485,8 @@ function lowerAssignment(
|
||||
switch (lvalueNode.type) {
|
||||
case "Identifier": {
|
||||
const lvalue = lvaluePath as NodePath<t.Identifier>;
|
||||
const identifier = builder.resolveIdentifier(lvalue);
|
||||
if (identifier == null) {
|
||||
if (kind === InstructionKind.Reassign) {
|
||||
// Trying to reassign a global is not allowed
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
nodePath: lvalue,
|
||||
});
|
||||
} else {
|
||||
// Else its an internal error bc we couldn't find the binding
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
nodePath: lvalue,
|
||||
});
|
||||
}
|
||||
const place = lowerIdentifierForAssignment(builder, loc, kind, lvalue);
|
||||
if (place === null) {
|
||||
return {
|
||||
kind: "UnsupportedNode",
|
||||
loc: lvalue.node.loc ?? GeneratedSource,
|
||||
@@ -2474,13 +2494,6 @@ function lowerAssignment(
|
||||
};
|
||||
}
|
||||
|
||||
const place: Place = {
|
||||
kind: "Identifier",
|
||||
identifier: identifier,
|
||||
effect: Effect.Unknown,
|
||||
loc: lvalue.node.loc ?? GeneratedSource,
|
||||
};
|
||||
|
||||
let temporary;
|
||||
if (builder.isContextIdentifier(lvalue)) {
|
||||
if (kind !== InstructionKind.Reassign) {
|
||||
@@ -2585,13 +2598,29 @@ function lowerAssignment(
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const identifier = lowerIdentifier(builder, argument);
|
||||
const identifier = lowerIdentifierForAssignment(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource,
|
||||
kind,
|
||||
argument
|
||||
);
|
||||
if (identifier === null) {
|
||||
continue;
|
||||
}
|
||||
items.push({
|
||||
kind: "Spread",
|
||||
place: identifier,
|
||||
});
|
||||
} else if (element.isIdentifier()) {
|
||||
const identifier = lowerIdentifier(builder, element);
|
||||
const identifier = lowerIdentifierForAssignment(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource,
|
||||
kind,
|
||||
element
|
||||
);
|
||||
if (identifier === null) {
|
||||
continue;
|
||||
}
|
||||
items.push(identifier);
|
||||
} else {
|
||||
const temp = buildTemporaryPlace(
|
||||
@@ -2636,7 +2665,15 @@ function lowerAssignment(
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const identifier = lowerIdentifier(builder, argument);
|
||||
const identifier = lowerIdentifierForAssignment(
|
||||
builder,
|
||||
property.node.loc ?? GeneratedSource,
|
||||
kind,
|
||||
argument
|
||||
);
|
||||
if (identifier === null) {
|
||||
continue;
|
||||
}
|
||||
properties.push({
|
||||
kind: "Spread",
|
||||
place: identifier,
|
||||
@@ -2678,7 +2715,15 @@ function lowerAssignment(
|
||||
continue;
|
||||
}
|
||||
if (element.isIdentifier()) {
|
||||
const identifier = lowerIdentifier(builder, element);
|
||||
const identifier = lowerIdentifierForAssignment(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource,
|
||||
kind,
|
||||
element
|
||||
);
|
||||
if (identifier === null) {
|
||||
continue;
|
||||
}
|
||||
properties.push({
|
||||
kind: "ObjectProperty",
|
||||
name: key.node.name,
|
||||
|
||||
@@ -88,7 +88,7 @@ class SSABuilder {
|
||||
CompilerError.invariant(
|
||||
`EnterSSA: Expected identifier to be defined before being used`,
|
||||
oldPlace.loc,
|
||||
`Identifier ${printIdentifier(oldId)} is undfined`
|
||||
`Identifier ${printIdentifier(oldId)} is undefined`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function useFoo(props) {
|
||||
[x] = props;
|
||||
return { x };
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function useFoo(props) {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = { x };
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let a;
|
||||
[a, b] = props.value;
|
||||
|
||||
return [a, b];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(2);
|
||||
|
||||
const [a] = props.value;
|
||||
const c_0 = $[0] !== a;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = [a, b];
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ function component(a) {
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$6 is undfined (4:4)
|
||||
[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$6 is undefined
|
||||
```
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function useFoo(props) {
|
||||
[x] = props;
|
||||
return { x };
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
|
||||
1 | function useFoo(props) {
|
||||
> 2 | [x] = props;
|
||||
| ^
|
||||
3 | return { x };
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let a;
|
||||
[a, b] = props.value;
|
||||
|
||||
return [a, b];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
|
||||
1 | function Component(props) {
|
||||
2 | let a;
|
||||
> 3 | [a, b] = props.value;
|
||||
| ^
|
||||
4 |
|
||||
5 | return [a, b];
|
||||
6 | }
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ function component(a) {
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$2 is undfined (7:7)
|
||||
[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$2 is undefined (7:7)
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user