mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Support for (Optional)MemberExpression callee in OptionalCall
Earlier PRs bailed out when the callee of an OptionalCallExpression was a MemberExpression or OptionalMemberExpression (ie for optional method calls). This PRs expands support for optional method calls, including when the receiver, method, or both are optional. Even better, we don't need to add any additional terminals or instruction variants for this case - the one new OptionalCall terminal from earlier in the stack works for all these cases.
This commit is contained in:
@@ -943,19 +943,6 @@ function lowerExpression(
|
||||
const expr = exprPath as NodePath<t.OptionalCallExpression>;
|
||||
const optional = expr.node.optional;
|
||||
const calleePath = expr.get("callee");
|
||||
if (
|
||||
calleePath.isMemberExpression() ||
|
||||
calleePath.isOptionalMemberExpression()
|
||||
) {
|
||||
// collect any errors from the arguments
|
||||
lowerArguments(builder, expr.get("arguments"));
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerExpression) Support OptionalCallExpression with ${calleePath.type} callee`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: calleePath,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
const loc = expr.node.loc ?? GeneratedSource;
|
||||
const place = buildTemporaryPlace(builder, loc);
|
||||
const continuationBlock = builder.reserve(builder.currentBlockKind());
|
||||
@@ -963,24 +950,64 @@ function lowerExpression(
|
||||
// Lower the callee in the current block: the callee is always unconditionally evaluated
|
||||
// The test block's branch will test on this value to determine whether to evaluate the call (consequent)
|
||||
// or evaluate to undefined (alternate)
|
||||
const callee = lowerExpressionToTemporary(builder, calleePath);
|
||||
let callee:
|
||||
| { kind: "CallExpression"; callee: Place }
|
||||
| { kind: "MethodCall"; receiver: Place; property: Place };
|
||||
if (
|
||||
calleePath.isMemberExpression() ||
|
||||
calleePath.isOptionalMemberExpression()
|
||||
) {
|
||||
const memberExpr = lowerMemberExpression(builder, calleePath);
|
||||
const propertyPlace = buildTemporaryPlace(builder, GeneratedSource);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...propertyPlace },
|
||||
value: memberExpr.value,
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
callee = {
|
||||
kind: "MethodCall",
|
||||
receiver: memberExpr.object,
|
||||
property: propertyPlace,
|
||||
};
|
||||
} else {
|
||||
callee = {
|
||||
kind: "CallExpression",
|
||||
callee: lowerExpressionToTemporary(builder, calleePath),
|
||||
};
|
||||
}
|
||||
|
||||
// block to evaluate if the callee is non-null/undefined. arguments are lowered in this block to preserve
|
||||
// the semantic of conditional evaluation depending on the callee
|
||||
const consequent = builder.enter("value", () => {
|
||||
const args = lowerArguments(builder, expr.get("arguments"));
|
||||
const temp = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
value: {
|
||||
kind: "CallExpression",
|
||||
callee: { ...callee },
|
||||
args,
|
||||
if (callee.kind === "CallExpression") {
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
value: {
|
||||
kind: "CallExpression",
|
||||
callee: { ...callee.callee },
|
||||
args,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
value: {
|
||||
kind: "MethodCall",
|
||||
receiver: { ...callee.receiver },
|
||||
property: { ...callee.property },
|
||||
args,
|
||||
loc: exprLoc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
}
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: buildTemporaryPlace(builder, loc),
|
||||
@@ -1033,9 +1060,11 @@ function lowerExpression(
|
||||
});
|
||||
|
||||
const testBlock = builder.enter("value", () => {
|
||||
const testPlace =
|
||||
callee.kind === "CallExpression" ? callee.callee : callee.property;
|
||||
return {
|
||||
kind: "branch",
|
||||
test: { ...callee },
|
||||
test: { ...testPlace },
|
||||
consequent,
|
||||
alternate,
|
||||
id: makeInstructionId(0),
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
const x = makeObject(props);
|
||||
const y = makeObject(props);
|
||||
t0 = x.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function Component(props) {
|
||||
const x = makeObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x?.method(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
t0 = x?.method(y.a, props.a, foo(y.b), bar(props.b));
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function Component(props) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x?.method(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x?.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
t0 = x?.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function Component(props) {
|
||||
const x = makeOptionalObject(props);
|
||||
const y = makeObject(props);
|
||||
const z = x?.optionalMethod?.(y.a, props.a, foo(y.b), bar(props.b));
|
||||
return z;
|
||||
}
|
||||
Reference in New Issue
Block a user