Compare commits

..

1 Commits

Author SHA1 Message Date
Joe Savona
107d1983fa [compiler] fix bad rebase from sapling 2025-11-14 14:48:20 -08:00
7 changed files with 36 additions and 307 deletions

View File

@@ -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> {

View File

@@ -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);
}

View File

@@ -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;
}
/*

View File

@@ -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]

View File

@@ -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',
},
],
};

View File

@@ -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);

View File

@@ -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(