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:
Joe Savona
2023-06-04 11:29:55 -07:00
parent 2ae3592d29
commit e41a8d6a1e
10 changed files with 129 additions and 95 deletions
+73 -28
View File
@@ -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,
+1 -1
View File
@@ -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`
);
}
@@ -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;
}
```
@@ -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;
}
```
@@ -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
```
@@ -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 |
```
@@ -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 | }
```
@@ -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)
```