Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
107d1983fa |
@@ -11,7 +11,6 @@ import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
Instruction,
|
||||
InstructionKind,
|
||||
InstructionValue,
|
||||
makeInstructionId,
|
||||
Pattern,
|
||||
@@ -33,32 +32,6 @@ export function* eachInstructionLValue(
|
||||
yield* eachInstructionValueLValue(instr.value);
|
||||
}
|
||||
|
||||
export function* eachInstructionLValueWithKind(
|
||||
instr: ReactiveInstruction,
|
||||
): Iterable<[Place, InstructionKind]> {
|
||||
switch (instr.value.kind) {
|
||||
case 'DeclareContext':
|
||||
case 'StoreContext':
|
||||
case 'DeclareLocal':
|
||||
case 'StoreLocal': {
|
||||
yield [instr.value.lvalue.place, instr.value.lvalue.kind];
|
||||
break;
|
||||
}
|
||||
case 'Destructure': {
|
||||
const kind = instr.value.lvalue.kind;
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
yield [place, kind];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'PostfixUpdate':
|
||||
case 'PrefixUpdate': {
|
||||
yield [instr.value.lvalue, InstructionKind.Reassign];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function* eachInstructionValueLValue(
|
||||
value: ReactiveValue,
|
||||
): Iterable<Place> {
|
||||
|
||||
@@ -1359,6 +1359,8 @@ function codegenInstructionNullable(
|
||||
value = null;
|
||||
} else {
|
||||
lvalue = instr.value.lvalue.pattern;
|
||||
let hasReassign = false;
|
||||
let hasDeclaration = false;
|
||||
for (const place of eachPatternOperand(lvalue)) {
|
||||
if (
|
||||
kind !== InstructionKind.Reassign &&
|
||||
@@ -1366,6 +1368,26 @@ function codegenInstructionNullable(
|
||||
) {
|
||||
cx.temp.set(place.identifier.declarationId, null);
|
||||
}
|
||||
const isDeclared = cx.hasDeclared(place.identifier);
|
||||
hasReassign ||= isDeclared;
|
||||
hasDeclaration ||= !isDeclared;
|
||||
}
|
||||
if (hasReassign && hasDeclaration) {
|
||||
CompilerError.invariant(false, {
|
||||
reason:
|
||||
'Encountered a destructuring operation where some identifiers are already declared (reassignments) but others are not (declarations)',
|
||||
description: null,
|
||||
details: [
|
||||
{
|
||||
kind: 'error',
|
||||
loc: instr.loc,
|
||||
message: null,
|
||||
},
|
||||
],
|
||||
suggestions: null,
|
||||
});
|
||||
} else if (hasReassign) {
|
||||
kind = InstructionKind.Reassign;
|
||||
}
|
||||
value = codegenPlaceToExpression(cx, instr.value.value);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,7 @@ import {
|
||||
promoteTemporary,
|
||||
} from '../HIR';
|
||||
import {clonePlaceToTemporary} from '../HIR/HIRBuilder';
|
||||
import {
|
||||
eachInstructionLValueWithKind,
|
||||
eachPatternOperand,
|
||||
mapPatternOperands,
|
||||
} from '../HIR/visitors';
|
||||
import {eachPatternOperand, mapPatternOperands} from '../HIR/visitors';
|
||||
import {
|
||||
ReactiveFunctionTransform,
|
||||
Transformed,
|
||||
@@ -117,9 +113,6 @@ class Visitor extends ReactiveFunctionTransform<State> {
|
||||
): Transformed<ReactiveStatement> {
|
||||
this.visitInstruction(instruction, state);
|
||||
|
||||
let instructionsToProcess: Array<ReactiveInstruction> = [instruction];
|
||||
let result: Transformed<ReactiveStatement> = {kind: 'keep'};
|
||||
|
||||
if (instruction.value.kind === 'Destructure') {
|
||||
const transformed = transformDestructuring(
|
||||
state,
|
||||
@@ -127,8 +120,7 @@ class Visitor extends ReactiveFunctionTransform<State> {
|
||||
instruction.value,
|
||||
);
|
||||
if (transformed) {
|
||||
instructionsToProcess = transformed;
|
||||
result = {
|
||||
return {
|
||||
kind: 'replace-many',
|
||||
value: transformed.map(instruction => ({
|
||||
kind: 'instruction',
|
||||
@@ -137,17 +129,7 @@ class Visitor extends ReactiveFunctionTransform<State> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Update state.declared with declarations from the instruction(s)
|
||||
for (const instr of instructionsToProcess) {
|
||||
for (const [place, kind] of eachInstructionLValueWithKind(instr)) {
|
||||
if (kind !== InstructionKind.Reassign) {
|
||||
state.declared.add(place.identifier.declarationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return {kind: 'keep'};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,13 +144,10 @@ function transformDestructuring(
|
||||
const isDeclared = state.declared.has(place.identifier.declarationId);
|
||||
if (isDeclared) {
|
||||
reassigned.add(place.identifier.id);
|
||||
} else {
|
||||
hasDeclaration = true;
|
||||
}
|
||||
hasDeclaration ||= !isDeclared;
|
||||
}
|
||||
if (!hasDeclaration) {
|
||||
// all reassignments
|
||||
destructure.lvalue.kind = InstructionKind.Reassign;
|
||||
if (reassigned.size === 0 || !hasDeclaration) {
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @compilationMode:"infer"
|
||||
'use strict';
|
||||
|
||||
function getWeekendDays(user) {
|
||||
return [0, 6];
|
||||
}
|
||||
|
||||
function getConfig(weekendDays) {
|
||||
return [1, 5];
|
||||
}
|
||||
|
||||
component Calendar(user, defaultFirstDay, currentDate, view) {
|
||||
const weekendDays = getWeekendDays(user);
|
||||
let firstDay = defaultFirstDay;
|
||||
let daysToDisplay = 7;
|
||||
if (view === 'week') {
|
||||
let lastDay;
|
||||
// this assignment produces invalid code
|
||||
[firstDay, lastDay] = getConfig(weekendDays);
|
||||
daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1;
|
||||
} else if (view === 'day') {
|
||||
firstDay = currentDate.getDayOfWeek();
|
||||
daysToDisplay = 1;
|
||||
}
|
||||
|
||||
return [currentDate, firstDay, daysToDisplay];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Calendar,
|
||||
params: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'week',
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'week',
|
||||
},
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'day',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
"use strict";
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
|
||||
function getWeekendDays(user) {
|
||||
return [0, 6];
|
||||
}
|
||||
|
||||
function getConfig(weekendDays) {
|
||||
return [1, 5];
|
||||
}
|
||||
|
||||
function Calendar(t0) {
|
||||
const $ = _c(12);
|
||||
const { user, defaultFirstDay, currentDate, view } = t0;
|
||||
let daysToDisplay;
|
||||
let firstDay;
|
||||
if (
|
||||
$[0] !== currentDate ||
|
||||
$[1] !== defaultFirstDay ||
|
||||
$[2] !== user ||
|
||||
$[3] !== view
|
||||
) {
|
||||
const weekendDays = getWeekendDays(user);
|
||||
firstDay = defaultFirstDay;
|
||||
daysToDisplay = 7;
|
||||
if (view === "week") {
|
||||
let lastDay;
|
||||
|
||||
[firstDay, lastDay] = getConfig(weekendDays);
|
||||
daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1;
|
||||
} else {
|
||||
if (view === "day") {
|
||||
let t1;
|
||||
if ($[6] !== currentDate) {
|
||||
t1 = currentDate.getDayOfWeek();
|
||||
$[6] = currentDate;
|
||||
$[7] = t1;
|
||||
} else {
|
||||
t1 = $[7];
|
||||
}
|
||||
firstDay = t1;
|
||||
daysToDisplay = 1;
|
||||
}
|
||||
}
|
||||
$[0] = currentDate;
|
||||
$[1] = defaultFirstDay;
|
||||
$[2] = user;
|
||||
$[3] = view;
|
||||
$[4] = daysToDisplay;
|
||||
$[5] = firstDay;
|
||||
} else {
|
||||
daysToDisplay = $[4];
|
||||
firstDay = $[5];
|
||||
}
|
||||
let t1;
|
||||
if ($[8] !== currentDate || $[9] !== daysToDisplay || $[10] !== firstDay) {
|
||||
t1 = [currentDate, firstDay, daysToDisplay];
|
||||
$[8] = currentDate;
|
||||
$[9] = daysToDisplay;
|
||||
$[10] = firstDay;
|
||||
$[11] = t1;
|
||||
} else {
|
||||
t1 = $[11];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Calendar,
|
||||
params: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: { getDayOfWeek: () => 3 },
|
||||
view: "week",
|
||||
},
|
||||
],
|
||||
|
||||
sequentialRenders: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: { getDayOfWeek: () => 3 },
|
||||
view: "week",
|
||||
},
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: { getDayOfWeek: () => 3 },
|
||||
view: "day",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [{"getDayOfWeek":"[[ function params=0 ]]"},1,5]
|
||||
[{"getDayOfWeek":"[[ function params=0 ]]"},3,1]
|
||||
@@ -1,53 +0,0 @@
|
||||
// @flow @compilationMode:"infer"
|
||||
'use strict';
|
||||
|
||||
function getWeekendDays(user) {
|
||||
return [0, 6];
|
||||
}
|
||||
|
||||
function getConfig(weekendDays) {
|
||||
return [1, 5];
|
||||
}
|
||||
|
||||
component Calendar(user, defaultFirstDay, currentDate, view) {
|
||||
const weekendDays = getWeekendDays(user);
|
||||
let firstDay = defaultFirstDay;
|
||||
let daysToDisplay = 7;
|
||||
if (view === 'week') {
|
||||
let lastDay;
|
||||
// this assignment produces invalid code
|
||||
[firstDay, lastDay] = getConfig(weekendDays);
|
||||
daysToDisplay = ((7 + lastDay - firstDay) % 7) + 1;
|
||||
} else if (view === 'day') {
|
||||
firstDay = currentDate.getDayOfWeek();
|
||||
daysToDisplay = 1;
|
||||
}
|
||||
|
||||
return [currentDate, firstDay, daysToDisplay];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Calendar,
|
||||
params: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'week',
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'week',
|
||||
},
|
||||
{
|
||||
user: {},
|
||||
defaultFirstDay: 1,
|
||||
currentDate: {getDayOfWeek: () => 3},
|
||||
view: 'day',
|
||||
},
|
||||
],
|
||||
};
|
||||
15
packages/react-client/src/ReactFlightClient.js
vendored
15
packages/react-client/src/ReactFlightClient.js
vendored
@@ -813,12 +813,6 @@ function createInitializedStreamChunk<
|
||||
value: T,
|
||||
controller: FlightStreamController,
|
||||
): InitializedChunk<T> {
|
||||
if (__DEV__) {
|
||||
// Retain a strong reference to the Response while we wait for chunks.
|
||||
if (response._pendingChunks++ === 0) {
|
||||
response._weakResponse.response = response;
|
||||
}
|
||||
}
|
||||
// We use the reason field to stash the controller since we already have that
|
||||
// field. It's a bit of a hack but efficient.
|
||||
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
||||
@@ -3081,6 +3075,7 @@ function resolveStream<T: ReadableStream | $AsyncIterable<any, any, void>>(
|
||||
// We already resolved. We didn't expect to see this.
|
||||
return;
|
||||
}
|
||||
releasePendingChunk(response, chunk);
|
||||
|
||||
const resolveListeners = chunk.value;
|
||||
|
||||
@@ -3380,14 +3375,6 @@ function stopStream(
|
||||
// We didn't expect not to have an existing stream;
|
||||
return;
|
||||
}
|
||||
if (__DEV__) {
|
||||
if (--response._pendingChunks === 0) {
|
||||
// We're no longer waiting for any more chunks. We can release the strong
|
||||
// reference to the response. We'll regain it if we ask for any more data
|
||||
// later on.
|
||||
response._weakResponse.response = null;
|
||||
}
|
||||
}
|
||||
const streamChunk: InitializedStreamChunk<any> = (chunk: any);
|
||||
const controller = streamChunk.reason;
|
||||
controller.close(row === '' ? '"$undefined"' : row);
|
||||
|
||||
33
packages/react-server/src/ReactFlightServer.js
vendored
33
packages/react-server/src/ReactFlightServer.js
vendored
@@ -339,10 +339,8 @@ function isPromiseAwaitInternal(url: string, functionName: string): boolean {
|
||||
case 'Function.resolve':
|
||||
case 'Function.all':
|
||||
case 'Function.allSettled':
|
||||
case 'Function.any':
|
||||
case 'Function.race':
|
||||
case 'Function.try':
|
||||
case 'Function.withResolvers':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -2349,8 +2347,7 @@ function visitAsyncNodeImpl(
|
||||
// The technique for debugging the effects of uncached data on the render is to simply uncache it.
|
||||
return null;
|
||||
}
|
||||
|
||||
let previousIONode: void | null | PromiseNode | IONode = null;
|
||||
let previousIONode = null;
|
||||
// First visit anything that blocked this sequence to start in the first place.
|
||||
if (node.previous !== null) {
|
||||
previousIONode = visitAsyncNode(
|
||||
@@ -2366,20 +2363,12 @@ function visitAsyncNodeImpl(
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// `found` represents the return value of the following switch statement.
|
||||
// We can't use multiple `return` statements in the switch statement
|
||||
// since that prevents Closure compiler from inlining `visitAsyncImpl`
|
||||
// thus doubling the call stack size.
|
||||
let found: void | null | PromiseNode | IONode;
|
||||
switch (node.tag) {
|
||||
case IO_NODE: {
|
||||
found = node;
|
||||
break;
|
||||
return node;
|
||||
}
|
||||
case UNRESOLVED_PROMISE_NODE: {
|
||||
found = previousIONode;
|
||||
break;
|
||||
return previousIONode;
|
||||
}
|
||||
case PROMISE_NODE: {
|
||||
const awaited = node.awaited;
|
||||
@@ -2390,8 +2379,7 @@ function visitAsyncNodeImpl(
|
||||
if (ioNode === undefined) {
|
||||
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
|
||||
// further aborted nodes.
|
||||
found = undefined;
|
||||
break;
|
||||
return undefined;
|
||||
} else if (ioNode !== null) {
|
||||
// This Promise was blocked on I/O. That's a signal that this Promise is interesting to log.
|
||||
// We don't log it yet though. We return it to be logged by the point where it's awaited.
|
||||
@@ -2448,12 +2436,10 @@ function visitAsyncNodeImpl(
|
||||
forwardDebugInfo(request, task, debugInfo);
|
||||
}
|
||||
}
|
||||
found = match;
|
||||
break;
|
||||
return match;
|
||||
}
|
||||
case UNRESOLVED_AWAIT_NODE: {
|
||||
found = previousIONode;
|
||||
break;
|
||||
return previousIONode;
|
||||
}
|
||||
case AWAIT_NODE: {
|
||||
const awaited = node.awaited;
|
||||
@@ -2463,8 +2449,7 @@ function visitAsyncNodeImpl(
|
||||
if (ioNode === undefined) {
|
||||
// Undefined is used as a signal that we found a suitable aborted node and we don't have to find
|
||||
// further aborted nodes.
|
||||
found = undefined;
|
||||
break;
|
||||
return undefined;
|
||||
} else if (ioNode !== null) {
|
||||
const startTime: number = node.start;
|
||||
const endTime: number = node.end;
|
||||
@@ -2560,15 +2545,13 @@ function visitAsyncNodeImpl(
|
||||
forwardDebugInfo(request, task, debugInfo);
|
||||
}
|
||||
}
|
||||
found = match;
|
||||
break;
|
||||
return match;
|
||||
}
|
||||
default: {
|
||||
// eslint-disable-next-line react-internal/prod-error-codes
|
||||
throw new Error('Unknown AsyncSequence tag. This is a bug in React.');
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
function emitAsyncSequence(
|
||||
|
||||
Reference in New Issue
Block a user