mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Update on "[compiler] Override type provider for hook-like names"
If an imported name is hook-like but a type provider provides a non-hook type, we now override the returned value and treat it as a generic custom hook. This is meant as an extra check to prevent miscompilation. [ghstack-poisoned]
This commit is contained in:
+41
-12
@@ -1180,18 +1180,6 @@ function inferBlock(
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'TaggedTemplateExpression': {
|
||||
valueKind = {
|
||||
kind: ValueKind.Mutable,
|
||||
reason: new Set([ValueReason.Other]),
|
||||
context: new Set(),
|
||||
};
|
||||
effect = {
|
||||
kind: Effect.ConditionallyMutate,
|
||||
reason: ValueReason.Other,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'TemplateLiteral': {
|
||||
/*
|
||||
* template literal (with no tag function) always produces
|
||||
@@ -1312,6 +1300,47 @@ function inferBlock(
|
||||
instr.lvalue.effect = Effect.Store;
|
||||
continue;
|
||||
}
|
||||
case 'TaggedTemplateExpression': {
|
||||
const operands = [...eachInstructionValueOperand(instrValue)];
|
||||
if (operands.length !== 1) {
|
||||
// future-proofing to make sure we update this case when we support interpolation
|
||||
CompilerError.throwTodo({
|
||||
reason: 'Support tagged template expressions with interpolations',
|
||||
loc: instrValue.loc,
|
||||
});
|
||||
}
|
||||
const signature = getFunctionCallSignature(
|
||||
env,
|
||||
instrValue.tag.identifier.type,
|
||||
);
|
||||
let calleeEffect =
|
||||
signature?.calleeEffect ?? Effect.ConditionallyMutate;
|
||||
const returnValueKind: AbstractValue =
|
||||
signature !== null
|
||||
? {
|
||||
kind: signature.returnValueKind,
|
||||
reason: new Set([
|
||||
signature.returnValueReason ??
|
||||
ValueReason.KnownReturnSignature,
|
||||
]),
|
||||
context: new Set(),
|
||||
}
|
||||
: {
|
||||
kind: ValueKind.Mutable,
|
||||
reason: new Set([ValueReason.Other]),
|
||||
context: new Set(),
|
||||
};
|
||||
state.referenceAndRecordEffects(
|
||||
instrValue.tag,
|
||||
calleeEffect,
|
||||
ValueReason.Other,
|
||||
functionEffects,
|
||||
);
|
||||
state.initialize(instrValue, returnValueKind);
|
||||
state.define(instr.lvalue, instrValue);
|
||||
instr.lvalue.effect = Effect.ConditionallyMutate;
|
||||
continue;
|
||||
}
|
||||
case 'CallExpression': {
|
||||
const signature = getFunctionCallSignature(
|
||||
env,
|
||||
|
||||
+2
-2
@@ -227,6 +227,7 @@ function mayAllocate(env: Environment, instruction: Instruction): boolean {
|
||||
case 'StoreGlobal': {
|
||||
return false;
|
||||
}
|
||||
case 'TaggedTemplateExpression':
|
||||
case 'CallExpression':
|
||||
case 'MethodCall': {
|
||||
return instruction.lvalue.identifier.type.kind !== 'Primitive';
|
||||
@@ -241,8 +242,7 @@ function mayAllocate(env: Environment, instruction: Instruction): boolean {
|
||||
case 'ObjectExpression':
|
||||
case 'UnsupportedNode':
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression':
|
||||
case 'TaggedTemplateExpression': {
|
||||
case 'FunctionExpression': {
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
|
||||
+30
-5
@@ -671,12 +671,11 @@ function computeMemoizationInputs(
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'CallExpression': {
|
||||
case 'TaggedTemplateExpression': {
|
||||
const signature = getFunctionCallSignature(
|
||||
env,
|
||||
value.callee.identifier.type,
|
||||
value.tag.identifier.type,
|
||||
);
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
let lvalues = [];
|
||||
if (lvalue !== null) {
|
||||
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
|
||||
@@ -687,6 +686,33 @@ function computeMemoizationInputs(
|
||||
rvalues: [],
|
||||
};
|
||||
}
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
lvalues.push(
|
||||
...operands
|
||||
.filter(operand => isMutableEffect(operand.effect, operand.loc))
|
||||
.map(place => ({place, level: MemoizationLevel.Memoized})),
|
||||
);
|
||||
return {
|
||||
lvalues,
|
||||
rvalues: operands,
|
||||
};
|
||||
}
|
||||
case 'CallExpression': {
|
||||
const signature = getFunctionCallSignature(
|
||||
env,
|
||||
value.callee.identifier.type,
|
||||
);
|
||||
let lvalues = [];
|
||||
if (lvalue !== null) {
|
||||
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
|
||||
}
|
||||
if (signature?.noAlias === true) {
|
||||
return {
|
||||
lvalues,
|
||||
rvalues: [],
|
||||
};
|
||||
}
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
lvalues.push(
|
||||
...operands
|
||||
.filter(operand => isMutableEffect(operand.effect, operand.loc))
|
||||
@@ -702,7 +728,6 @@ function computeMemoizationInputs(
|
||||
env,
|
||||
value.property.identifier.type,
|
||||
);
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
let lvalues = [];
|
||||
if (lvalue !== null) {
|
||||
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
|
||||
@@ -713,6 +738,7 @@ function computeMemoizationInputs(
|
||||
rvalues: [],
|
||||
};
|
||||
}
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
lvalues.push(
|
||||
...operands
|
||||
.filter(operand => isMutableEffect(operand.effect, operand.loc))
|
||||
@@ -726,7 +752,6 @@ function computeMemoizationInputs(
|
||||
case 'RegExpLiteral':
|
||||
case 'ObjectMethod':
|
||||
case 'FunctionExpression':
|
||||
case 'TaggedTemplateExpression':
|
||||
case 'ArrayExpression':
|
||||
case 'NewExpression':
|
||||
case 'ObjectExpression':
|
||||
|
||||
@@ -250,6 +250,7 @@ function* generateInstructionTypes(
|
||||
}
|
||||
|
||||
case 'CallExpression': {
|
||||
const returnType = makeType();
|
||||
/*
|
||||
* TODO: callee could be a hook or a function, so this type equation isn't correct.
|
||||
* We should change Hook to a subtype of Function or change unifier logic.
|
||||
@@ -258,8 +259,25 @@ function* generateInstructionTypes(
|
||||
yield equation(value.callee.identifier.type, {
|
||||
kind: 'Function',
|
||||
shapeId: null,
|
||||
return: left,
|
||||
return: returnType,
|
||||
});
|
||||
yield equation(left, returnType);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'TaggedTemplateExpression': {
|
||||
const returnType = makeType();
|
||||
/*
|
||||
* TODO: callee could be a hook or a function, so this type equation isn't correct.
|
||||
* We should change Hook to a subtype of Function or change unifier logic.
|
||||
* (see https://github.com/facebook/react-forget/pull/1427)
|
||||
*/
|
||||
yield equation(value.tag.identifier.type, {
|
||||
kind: 'Function',
|
||||
shapeId: null,
|
||||
return: returnType,
|
||||
});
|
||||
yield equation(left, returnType);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -392,7 +410,6 @@ function* generateInstructionTypes(
|
||||
case 'MetaProperty':
|
||||
case 'ComputedStore':
|
||||
case 'ComputedLoad':
|
||||
case 'TaggedTemplateExpression':
|
||||
case 'Await':
|
||||
case 'GetIterator':
|
||||
case 'IteratorNext':
|
||||
|
||||
+8
@@ -161,6 +161,14 @@ function getContextReassignment(
|
||||
if (signature?.noAlias) {
|
||||
operands = [value.receiver, value.property];
|
||||
}
|
||||
} else if (value.kind === 'TaggedTemplateExpression') {
|
||||
const signature = getFunctionCallSignature(
|
||||
fn.env,
|
||||
value.tag.identifier.type,
|
||||
);
|
||||
if (signature?.noAlias) {
|
||||
operands = [value.tag];
|
||||
}
|
||||
}
|
||||
for (const operand of operands) {
|
||||
CompilerError.invariant(operand.effect !== Effect.Unknown, {
|
||||
|
||||
+35
-39
@@ -63,67 +63,63 @@ function useFragment(_arg1, _arg2) {
|
||||
}
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = graphql`
|
||||
const $ = _c(8);
|
||||
const post = useFragment(
|
||||
graphql`
|
||||
fragment F on T {
|
||||
id
|
||||
}
|
||||
`;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const post = useFragment(t0, props.post);
|
||||
let t1;
|
||||
if ($[1] !== post) {
|
||||
`,
|
||||
props.post,
|
||||
);
|
||||
let t0;
|
||||
if ($[0] !== post) {
|
||||
const allUrls = [];
|
||||
|
||||
const { media: t2, comments: t3, urls: t4 } = post;
|
||||
const media = t2 === undefined ? null : t2;
|
||||
const { media: t1, comments: t2, urls: t3 } = post;
|
||||
const media = t1 === undefined ? null : t1;
|
||||
let t4;
|
||||
if ($[2] !== t2) {
|
||||
t4 = t2 === undefined ? [] : t2;
|
||||
$[2] = t2;
|
||||
$[3] = t4;
|
||||
} else {
|
||||
t4 = $[3];
|
||||
}
|
||||
const comments = t4;
|
||||
let t5;
|
||||
if ($[3] !== t3) {
|
||||
if ($[4] !== t3) {
|
||||
t5 = t3 === undefined ? [] : t3;
|
||||
$[3] = t3;
|
||||
$[4] = t5;
|
||||
$[4] = t3;
|
||||
$[5] = t5;
|
||||
} else {
|
||||
t5 = $[4];
|
||||
t5 = $[5];
|
||||
}
|
||||
const comments = t5;
|
||||
const urls = t5;
|
||||
let t6;
|
||||
if ($[5] !== t4) {
|
||||
t6 = t4 === undefined ? [] : t4;
|
||||
$[5] = t4;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
}
|
||||
const urls = t6;
|
||||
let t7;
|
||||
if ($[7] !== comments.length) {
|
||||
t7 = (e) => {
|
||||
if ($[6] !== comments.length) {
|
||||
t6 = (e) => {
|
||||
if (!comments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(comments.length);
|
||||
};
|
||||
$[7] = comments.length;
|
||||
$[8] = t7;
|
||||
$[6] = comments.length;
|
||||
$[7] = t6;
|
||||
} else {
|
||||
t7 = $[8];
|
||||
t6 = $[7];
|
||||
}
|
||||
const onClick = t7;
|
||||
const onClick = t6;
|
||||
|
||||
allUrls.push(...urls);
|
||||
t1 = <Stringify media={media} allUrls={allUrls} onClick={onClick} />;
|
||||
$[1] = post;
|
||||
$[2] = t1;
|
||||
t0 = <Stringify media={media} allUrls={allUrls} onClick={onClick} />;
|
||||
$[0] = post;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
t0 = $[1];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {graphql} from 'shared-runtime';
|
||||
|
||||
export function Component({a, b}) {
|
||||
const fragment = graphql`
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
`;
|
||||
return <div>{fragment}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 1, b: 2},
|
||||
{a: 2, b: 2},
|
||||
{a: 3, b: 2},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { graphql } from "shared-runtime";
|
||||
|
||||
export function Component(t0) {
|
||||
const $ = _c(1);
|
||||
const fragment = graphql`
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
`;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <div>{fragment}</div>;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: 0, b: 0 }],
|
||||
sequentialRenders: [
|
||||
{ a: 0, b: 0 },
|
||||
{ a: 1, b: 0 },
|
||||
{ a: 1, b: 1 },
|
||||
{ a: 1, b: 2 },
|
||||
{ a: 2, b: 2 },
|
||||
{ a: 3, b: 2 },
|
||||
{ a: 0, b: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
</div>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import {graphql} from 'shared-runtime';
|
||||
|
||||
export function Component({a, b}) {
|
||||
const fragment = graphql`
|
||||
fragment Foo on User {
|
||||
name
|
||||
}
|
||||
`;
|
||||
return <div>{fragment}</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: 0, b: 0}],
|
||||
sequentialRenders: [
|
||||
{a: 0, b: 0},
|
||||
{a: 1, b: 0},
|
||||
{a: 1, b: 1},
|
||||
{a: 1, b: 2},
|
||||
{a: 2, b: 2},
|
||||
{a: 3, b: 2},
|
||||
{a: 0, b: 0},
|
||||
],
|
||||
};
|
||||
@@ -32,6 +32,14 @@ export function makeSharedRuntimeTypeProvider({
|
||||
returnType: {kind: 'type', name: 'Primitive'},
|
||||
returnValueKind: ValueKindEnum.Primitive,
|
||||
},
|
||||
graphql: {
|
||||
kind: 'function',
|
||||
calleeEffect: EffectEnum.Read,
|
||||
positionalParams: [],
|
||||
restParam: EffectEnum.Read,
|
||||
returnType: {kind: 'type', name: 'Primitive'},
|
||||
returnValueKind: ValueKindEnum.Primitive,
|
||||
},
|
||||
typedArrayPush: {
|
||||
kind: 'function',
|
||||
calleeEffect: EffectEnum.Read,
|
||||
|
||||
@@ -134,7 +134,7 @@ export const alwaysThrottleRetries = true;
|
||||
|
||||
export const passChildrenWhenCloningPersistedNodes = false;
|
||||
|
||||
export const enableServerComponentLogs = __EXPERIMENTAL__;
|
||||
export const enableServerComponentLogs = true;
|
||||
|
||||
/**
|
||||
* Enables a new Fiber flag used in persisted mode to reduce the number
|
||||
|
||||
Reference in New Issue
Block a user