mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
ReactiveTerminal: add loc and id to all variants
This commit is contained in:
@@ -156,28 +156,46 @@ export type ReactiveTerminal =
|
||||
| ReactiveLabelTerminal
|
||||
| ReactiveTryTerminal;
|
||||
|
||||
function _staticInvariantReactiveTerminalHasLocation(
|
||||
terminal: ReactiveTerminal
|
||||
): SourceLocation {
|
||||
// If this fails, it is because a variant of ReactiveTerminal is missing a .loc - add it!
|
||||
return terminal.loc;
|
||||
}
|
||||
|
||||
function _staticInvariantReactiveTerminalHasInstructionId(
|
||||
terminal: ReactiveTerminal
|
||||
): InstructionId {
|
||||
// If this fails, it is because a variant of ReactiveTerminal is missing a .id - add it!
|
||||
return terminal.id;
|
||||
}
|
||||
|
||||
export type ReactiveTerminalTargetKind = "implicit" | "labeled" | "unlabeled";
|
||||
export type ReactiveBreakTerminal = {
|
||||
kind: "break";
|
||||
target: BlockId;
|
||||
id: InstructionId | null;
|
||||
id: InstructionId;
|
||||
targetKind: ReactiveTerminalTargetKind;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveContinueTerminal = {
|
||||
kind: "continue";
|
||||
target: BlockId;
|
||||
id: InstructionId;
|
||||
targetKind: ReactiveTerminalTargetKind;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveReturnTerminal = {
|
||||
kind: "return";
|
||||
value: Place;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveThrowTerminal = {
|
||||
kind: "throw";
|
||||
value: Place;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveSwitchTerminal = {
|
||||
kind: "switch";
|
||||
@@ -187,18 +205,21 @@ export type ReactiveSwitchTerminal = {
|
||||
block: ReactiveBlock | void;
|
||||
}>;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveDoWhileTerminal = {
|
||||
kind: "do-while";
|
||||
loop: ReactiveBlock;
|
||||
test: ReactiveValue;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveWhileTerminal = {
|
||||
kind: "while";
|
||||
test: ReactiveValue;
|
||||
loop: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveForTerminal = {
|
||||
kind: "for";
|
||||
@@ -207,18 +228,21 @@ export type ReactiveForTerminal = {
|
||||
update: ReactiveValue | null;
|
||||
loop: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveForOfTerminal = {
|
||||
kind: "for-of";
|
||||
init: ReactiveValue;
|
||||
loop: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveForInTerminal = {
|
||||
kind: "for-in";
|
||||
init: ReactiveValue;
|
||||
loop: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveIfTerminal = {
|
||||
kind: "if";
|
||||
@@ -226,11 +250,13 @@ export type ReactiveIfTerminal = {
|
||||
consequent: ReactiveBlock;
|
||||
alternate: ReactiveBlock | null;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveLabelTerminal = {
|
||||
kind: "label";
|
||||
block: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
export type ReactiveTryTerminal = {
|
||||
kind: "try";
|
||||
@@ -238,6 +264,7 @@ export type ReactiveTryTerminal = {
|
||||
handlerBinding: Place | null;
|
||||
handler: ReactiveBlock;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
// A function lowered to HIR form, ie where its body is lowered to an HIR control-flow graph
|
||||
|
||||
+68
-87
@@ -88,7 +88,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "return",
|
||||
// loc: terminal.loc,
|
||||
loc: terminal.loc,
|
||||
value: terminal.value,
|
||||
id: terminal.id,
|
||||
},
|
||||
@@ -101,6 +101,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "throw",
|
||||
loc: terminal.loc,
|
||||
value: terminal.value,
|
||||
id: terminal.id,
|
||||
},
|
||||
@@ -126,10 +127,10 @@ class Driver {
|
||||
|
||||
let consequent: ReactiveBlock | null = null;
|
||||
if (this.cx.isScheduled(terminal.consequent)) {
|
||||
const break_ = this.visitBreak(terminal.consequent, null);
|
||||
if (break_ !== null) {
|
||||
consequent = [break_];
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'if' where the consequent is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
} else {
|
||||
consequent = this.traverseBlock(
|
||||
this.cx.ir.blocks.get(terminal.consequent)!
|
||||
@@ -139,10 +140,10 @@ class Driver {
|
||||
let alternate: ReactiveBlock | null = null;
|
||||
if (alternateId !== null) {
|
||||
if (this.cx.isScheduled(alternateId)) {
|
||||
const break_ = this.visitBreak(alternateId, null);
|
||||
if (break_ !== null) {
|
||||
alternate = [break_];
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'if' where the alternate is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
} else {
|
||||
alternate = this.traverseBlock(this.cx.ir.blocks.get(alternateId)!);
|
||||
}
|
||||
@@ -153,6 +154,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "if",
|
||||
loc: terminal.loc,
|
||||
test: terminal.test,
|
||||
consequent: consequent ?? this.emptyBlock(),
|
||||
alternate: alternate,
|
||||
@@ -186,36 +188,16 @@ class Driver {
|
||||
test: Place | null;
|
||||
block: ReactiveBlock;
|
||||
}> = [];
|
||||
[...terminal.cases].reverse().forEach((case_, index) => {
|
||||
[...terminal.cases].reverse().forEach((case_, _index) => {
|
||||
const test = case_.test;
|
||||
|
||||
let consequent: ReactiveBlock;
|
||||
if (this.cx.isScheduled(case_.block)) {
|
||||
/*
|
||||
* cases which are empty or contain only a `break` may point to blocks
|
||||
* that are already scheduled. emit as follows:
|
||||
* - if the block is for another case branch, don't emit a break and fall-through
|
||||
* - else, emit an explicit break.
|
||||
*/
|
||||
const break_ = this.visitBreak(case_.block, null);
|
||||
if (
|
||||
index === 0 &&
|
||||
break_.terminal.targetKind === "implicit" &&
|
||||
case_.block === terminal.fallthrough &&
|
||||
case_.test === null
|
||||
) {
|
||||
/*
|
||||
* If the last case statement (first in reverse order) is a default that
|
||||
* jumps to the fallthrough, then we would emit a useless `default: {}`,
|
||||
* so instead skip this case.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const block = [];
|
||||
if (break_ !== null) {
|
||||
block.push(break_);
|
||||
}
|
||||
consequent = block;
|
||||
CompilerError.invariant(case_.block === terminal.fallthrough, {
|
||||
reason: `Unexpected 'switch' where a case is already scheduled and block is not the fallthrough`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
consequent = this.traverseBlock(
|
||||
this.cx.ir.blocks.get(case_.block)!
|
||||
@@ -232,6 +214,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "switch",
|
||||
loc: terminal.loc,
|
||||
test: terminal.test,
|
||||
cases,
|
||||
id: terminal.id,
|
||||
@@ -269,14 +252,10 @@ class Driver {
|
||||
if (loopId) {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'do-while' where the loop is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
const testValue = this.visitValueBlock(
|
||||
@@ -289,6 +268,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "do-while",
|
||||
loc: terminal.loc,
|
||||
test: testValue,
|
||||
loop: loopBody,
|
||||
id: terminal.id,
|
||||
@@ -333,14 +313,10 @@ class Driver {
|
||||
if (loopId) {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'while' where the loop is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
this.cx.unscheduleAll(scheduleIds);
|
||||
@@ -348,7 +324,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "while",
|
||||
// loc: terminal.loc,
|
||||
loc: terminal.loc,
|
||||
test: testValue,
|
||||
loop: loopBody,
|
||||
id: terminal.id,
|
||||
@@ -425,14 +401,10 @@ class Driver {
|
||||
if (loopId) {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'for' where the loop is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
this.cx.unscheduleAll(scheduleIds);
|
||||
@@ -440,6 +412,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "for",
|
||||
loc: terminal.loc,
|
||||
init: initValue,
|
||||
test: testValue,
|
||||
update: updateValue,
|
||||
@@ -505,14 +478,10 @@ class Driver {
|
||||
if (loopId) {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'for-of' where the loop is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
this.cx.unscheduleAll(scheduleIds);
|
||||
@@ -520,6 +489,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "for-of",
|
||||
loc: terminal.loc,
|
||||
init: initValue,
|
||||
loop: loopBody,
|
||||
id: terminal.id,
|
||||
@@ -583,14 +553,10 @@ class Driver {
|
||||
if (loopId) {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'for-in' where the loop is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
this.cx.unscheduleAll(scheduleIds);
|
||||
@@ -598,6 +564,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "for-in",
|
||||
loc: terminal.loc,
|
||||
init: initValue,
|
||||
loop: loopBody,
|
||||
id: terminal.id,
|
||||
@@ -615,7 +582,11 @@ class Driver {
|
||||
case "branch": {
|
||||
let consequent: ReactiveBlock | null = null;
|
||||
if (this.cx.isScheduled(terminal.consequent)) {
|
||||
const break_ = this.visitBreak(terminal.consequent, null);
|
||||
const break_ = this.visitBreak(
|
||||
terminal.consequent,
|
||||
terminal.id,
|
||||
terminal.loc
|
||||
);
|
||||
if (break_ !== null) {
|
||||
consequent = [break_];
|
||||
}
|
||||
@@ -627,10 +598,10 @@ class Driver {
|
||||
|
||||
let alternate: ReactiveBlock | null = null;
|
||||
if (this.cx.isScheduled(terminal.alternate)) {
|
||||
const break_ = this.visitBreak(terminal.alternate, null);
|
||||
if (break_ !== null) {
|
||||
alternate = [break_];
|
||||
}
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'branch' where the alternate is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
});
|
||||
} else {
|
||||
alternate = this.traverseBlock(
|
||||
this.cx.ir.blocks.get(terminal.alternate)!
|
||||
@@ -641,6 +612,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "if",
|
||||
loc: terminal.loc,
|
||||
test: terminal.test,
|
||||
consequent: consequent ?? this.emptyBlock(),
|
||||
alternate: alternate,
|
||||
@@ -664,15 +636,10 @@ class Driver {
|
||||
|
||||
let block: ReactiveBlock;
|
||||
if (this.cx.isScheduled(terminal.block)) {
|
||||
const break_ = this.visitBreak(terminal.block, null);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason:
|
||||
"Expected a break target for a label whose body is already scheduled",
|
||||
description: null,
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected 'label' where the block is already scheduled`,
|
||||
loc: terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
block = [break_];
|
||||
} else {
|
||||
block = this.traverseBlock(this.cx.ir.blocks.get(terminal.block)!);
|
||||
}
|
||||
@@ -682,6 +649,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "label",
|
||||
loc: terminal.loc,
|
||||
block,
|
||||
id: terminal.id,
|
||||
},
|
||||
@@ -730,14 +698,22 @@ class Driver {
|
||||
case "goto": {
|
||||
switch (terminal.variant) {
|
||||
case GotoVariant.Break: {
|
||||
const break_ = this.visitBreak(terminal.block, terminal.id);
|
||||
const break_ = this.visitBreak(
|
||||
terminal.block,
|
||||
terminal.id,
|
||||
terminal.loc
|
||||
);
|
||||
if (break_ !== null) {
|
||||
blockValue.push(break_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GotoVariant.Continue: {
|
||||
const continue_ = this.visitContinue(terminal.block, terminal.id);
|
||||
const continue_ = this.visitContinue(
|
||||
terminal.block,
|
||||
terminal.id,
|
||||
terminal.loc
|
||||
);
|
||||
if (continue_ !== null) {
|
||||
blockValue.push(continue_);
|
||||
}
|
||||
@@ -796,6 +772,7 @@ class Driver {
|
||||
: { id: fallthroughId, implicit: false },
|
||||
terminal: {
|
||||
kind: "try",
|
||||
loc: terminal.loc,
|
||||
block,
|
||||
handlerBinding: terminal.handlerBinding,
|
||||
handler,
|
||||
@@ -1134,7 +1111,8 @@ class Driver {
|
||||
|
||||
visitBreak(
|
||||
block: BlockId,
|
||||
id: InstructionId | null
|
||||
id: InstructionId,
|
||||
loc: SourceLocation
|
||||
): ReactiveTerminalStatement<ReactiveBreakTerminal> {
|
||||
const target = this.cx.getBreakTarget(block);
|
||||
if (target === null) {
|
||||
@@ -1149,6 +1127,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "break",
|
||||
loc,
|
||||
target: target.block,
|
||||
id,
|
||||
targetKind: target.type,
|
||||
@@ -1159,7 +1138,8 @@ class Driver {
|
||||
|
||||
visitContinue(
|
||||
block: BlockId,
|
||||
id: InstructionId
|
||||
id: InstructionId,
|
||||
loc: SourceLocation
|
||||
): ReactiveTerminalStatement<ReactiveContinueTerminal> {
|
||||
const target = this.cx.getContinueTarget(block);
|
||||
CompilerError.invariant(target !== null, {
|
||||
@@ -1173,6 +1153,7 @@ class Driver {
|
||||
kind: "terminal",
|
||||
terminal: {
|
||||
kind: "continue",
|
||||
loc,
|
||||
target: target.block,
|
||||
id,
|
||||
targetKind: target.type,
|
||||
|
||||
+3
@@ -9,6 +9,7 @@ import { visitReactiveFunction } from ".";
|
||||
import { CompilerError, Effect } from "..";
|
||||
import {
|
||||
Environment,
|
||||
GeneratedSource,
|
||||
InstructionKind,
|
||||
ReactiveFunction,
|
||||
ReactiveScope,
|
||||
@@ -245,6 +246,7 @@ class Transform extends ReactiveFunctionTransform<State> {
|
||||
terminal: {
|
||||
kind: "label",
|
||||
id: makeInstructionId(0),
|
||||
loc: GeneratedSource,
|
||||
block: instructions,
|
||||
},
|
||||
},
|
||||
@@ -319,6 +321,7 @@ class Transform extends ReactiveFunctionTransform<State> {
|
||||
terminal: {
|
||||
kind: "break",
|
||||
id: makeInstructionId(0),
|
||||
loc,
|
||||
targetKind: "labeled",
|
||||
target: earlyReturnValue.label,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user