mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[valueblocks] ReactiveFunction repr for logical expressions
Extends ReactiveInstruction's value type to be a regular InstructionValue *or* a LogicalValue. LogicalValue is operator, left, and right. It's really convenient that we've already distinguished Instruction/ReactiveInstruction now — while the _helpers_ here are updated to handle this new value type, the types ensure that HIR can never encounter a LogicalValue. The actual conversion of logical terminals into this value is complex and is later in the stack.
This commit is contained in:
@@ -934,6 +934,7 @@ function lowerExpression(
|
||||
id: makeInstructionId(0),
|
||||
test: testBlock.id,
|
||||
operator: expr.node.operator,
|
||||
loc: exprLoc,
|
||||
},
|
||||
testBlock
|
||||
);
|
||||
|
||||
@@ -89,7 +89,17 @@ export type ReactiveTerminalStatement<
|
||||
export type ReactiveInstruction = {
|
||||
id: InstructionId;
|
||||
lvalue: LValue | null;
|
||||
value: InstructionValue;
|
||||
value: ReactiveValue;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
export type ReactiveValue = InstructionValue | ReactiveLogicalValue;
|
||||
|
||||
export type ReactiveLogicalValue = {
|
||||
kind: "LogicalExpression";
|
||||
operator: t.LogicalExpression["operator"];
|
||||
left: ReactiveValue;
|
||||
right: ReactiveValue;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
@@ -298,6 +308,7 @@ export type LogicalTerminal = {
|
||||
test: BlockId;
|
||||
fallthrough: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
Place,
|
||||
ReactiveInstruction,
|
||||
ReactiveScope,
|
||||
ReactiveValue,
|
||||
SourceLocation,
|
||||
Terminal,
|
||||
Type,
|
||||
@@ -201,7 +202,7 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function printInstructionValue(instrValue: InstructionValue): string {
|
||||
export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
let value = "";
|
||||
switch (instrValue.kind) {
|
||||
case "ArrayExpression": {
|
||||
@@ -332,6 +333,12 @@ export function printInstructionValue(instrValue: InstructionValue): string {
|
||||
value = `${instrValue.tag}\`${instrValue.value.raw}\``;
|
||||
break;
|
||||
}
|
||||
case "LogicalExpression": {
|
||||
value = `Logical ${printInstructionValue(instrValue.left)} ${
|
||||
instrValue.operator
|
||||
} ${printInstructionValue(instrValue.right)}`;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instrValue,
|
||||
|
||||
@@ -291,6 +291,7 @@ export function mapTerminalSuccessors(
|
||||
fallthrough,
|
||||
operator: terminal.operator,
|
||||
id: makeInstructionId(0),
|
||||
loc: terminal.loc,
|
||||
};
|
||||
}
|
||||
case "return": {
|
||||
|
||||
@@ -19,9 +19,12 @@ import {
|
||||
ReactiveValueBlock,
|
||||
ScopeId,
|
||||
} from "../HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { eachTerminalBlock, mapTerminalBlocks } from "./visitors";
|
||||
import {
|
||||
eachReactiveValueOperand,
|
||||
eachTerminalBlock,
|
||||
mapTerminalBlocks,
|
||||
} from "./visitors";
|
||||
|
||||
/**
|
||||
* Note: this is the 4th of 4 passes that determine how to break a function into discrete
|
||||
@@ -222,7 +225,7 @@ export function getInstructionScope({
|
||||
if (lvalueScope !== null) {
|
||||
return lvalueScope;
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(value)) {
|
||||
for (const operand of eachReactiveValueOperand(value)) {
|
||||
const operandScope = getPlaceScope(id, operand);
|
||||
if (operandScope !== null) {
|
||||
return operandScope;
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
InstructionKind,
|
||||
InstructionValue,
|
||||
LValue,
|
||||
Place,
|
||||
ReactiveBlock,
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
ReactiveScope,
|
||||
ReactiveScopeDependency,
|
||||
ReactiveTerminal,
|
||||
ReactiveValue,
|
||||
ReactiveValueBlock,
|
||||
SourceLocation,
|
||||
} from "../HIR/HIR";
|
||||
@@ -430,6 +430,7 @@ const createLabelledStatement = withLoc(t.labeledStatement);
|
||||
const createVariableDeclaration = withLoc(t.variableDeclaration);
|
||||
const createWhileStatement = withLoc(t.whileStatement);
|
||||
const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
|
||||
const createLogicalExpression = withLoc(t.logicalExpression);
|
||||
|
||||
type Temporaries = Map<IdentifierId, t.Expression>;
|
||||
|
||||
@@ -482,7 +483,7 @@ function codegenInstruction(
|
||||
|
||||
function codegenInstructionValue(
|
||||
temp: Temporaries,
|
||||
instrValue: InstructionValue
|
||||
instrValue: ReactiveValue
|
||||
): t.Expression {
|
||||
let value: t.Expression;
|
||||
switch (instrValue.kind) {
|
||||
@@ -685,6 +686,15 @@ function codegenInstructionValue(
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "LogicalExpression": {
|
||||
value = createLogicalExpression(
|
||||
instrValue.loc,
|
||||
instrValue.operator,
|
||||
codegenInstructionValue(temp, instrValue.left),
|
||||
codegenInstructionValue(temp, instrValue.right)
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instrValue,
|
||||
|
||||
@@ -8,21 +8,24 @@
|
||||
import invariant from "invariant";
|
||||
import {
|
||||
InstructionId,
|
||||
InstructionValue,
|
||||
makeInstructionId,
|
||||
Place,
|
||||
ReactiveBlock,
|
||||
ReactiveFunction,
|
||||
ReactiveInstruction,
|
||||
ReactiveScope,
|
||||
ReactiveValue,
|
||||
ReactiveValueBlock,
|
||||
ScopeId,
|
||||
} from "../HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { retainWhere } from "../Utils/utils";
|
||||
import { getPlaceScope } from "./BuildReactiveBlocks";
|
||||
import { eachTerminalBlock, eachTerminalOperand } from "./visitors";
|
||||
import {
|
||||
eachReactiveValueOperand,
|
||||
eachTerminalBlock,
|
||||
eachTerminalOperand,
|
||||
} from "./visitors";
|
||||
|
||||
/**
|
||||
* Note: this is the 3rd of 4 passes that determine how to break a function into discrete
|
||||
@@ -184,10 +187,10 @@ function visitInstruction(
|
||||
function visitValue(
|
||||
context: Context,
|
||||
id: InstructionId,
|
||||
value: InstructionValue
|
||||
value: ReactiveValue
|
||||
): void {
|
||||
context.visitId(id);
|
||||
for (const operand of eachInstructionValueOperand(value)) {
|
||||
for (const operand of eachReactiveValueOperand(value)) {
|
||||
context.visitPlace(id, operand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Identifier,
|
||||
InstructionId,
|
||||
InstructionKind,
|
||||
InstructionValue,
|
||||
isPrimitiveType,
|
||||
LValue,
|
||||
makeInstructionId,
|
||||
@@ -19,10 +18,11 @@ import {
|
||||
ReactiveInstruction,
|
||||
ReactiveScope,
|
||||
ReactiveScopeDependency,
|
||||
ReactiveValue,
|
||||
ReactiveValueBlock,
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { eachReactiveValueOperand } from "./visitors";
|
||||
|
||||
/**
|
||||
* Infers the dependencies of each scope to include variables whose values
|
||||
@@ -266,19 +266,19 @@ function visitValueBlock(context: Context, block: ReactiveValueBlock): void {
|
||||
}
|
||||
}
|
||||
if (block.last !== null) {
|
||||
visitInstructionValue(context, block.last.value, null);
|
||||
visitReactiveValue(context, block.last.value, null);
|
||||
}
|
||||
}
|
||||
|
||||
function visitInstructionValue(
|
||||
function visitReactiveValue(
|
||||
context: Context,
|
||||
value: InstructionValue,
|
||||
value: ReactiveValue,
|
||||
lvalue: LValue | null
|
||||
): void {
|
||||
if (value.kind === "PropertyLoad" && lvalue !== null) {
|
||||
context.declareProperty(lvalue.place, value.object, value.property);
|
||||
} else {
|
||||
for (const operand of eachInstructionValueOperand(value)) {
|
||||
for (const operand of eachReactiveValueOperand(value)) {
|
||||
context.visitOperand(operand);
|
||||
}
|
||||
}
|
||||
@@ -286,7 +286,7 @@ function visitInstructionValue(
|
||||
|
||||
function visitInstruction(context: Context, instr: ReactiveInstruction): void {
|
||||
const { lvalue } = instr;
|
||||
visitInstructionValue(context, instr.value, lvalue);
|
||||
visitReactiveValue(context, instr.value, lvalue);
|
||||
if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
|
||||
const range = lvalue.place.identifier.mutableRange;
|
||||
// TODO: only assign Const if the value is never reassigned
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { eachReactiveValueOperand } from "./visitors";
|
||||
|
||||
/**
|
||||
* Ensures that each named variable in the given function has a unique name
|
||||
@@ -46,7 +47,7 @@ function visitBlockInner(scopes: Scopes, block: ReactiveBlock): void {
|
||||
for (const stmt of block) {
|
||||
switch (stmt.kind) {
|
||||
case "instruction": {
|
||||
for (const operand of eachInstructionValueOperand(
|
||||
for (const operand of eachReactiveValueOperand(
|
||||
stmt.instruction.value
|
||||
)) {
|
||||
scopes.visit(operand.identifier);
|
||||
@@ -76,7 +77,7 @@ function visitValueBlock(scopes: Scopes, block: ReactiveValueBlock): void {
|
||||
stmt.kind === "instruction",
|
||||
"Value blocks may only contain instructions"
|
||||
);
|
||||
for (const operand of eachInstructionValueOperand(stmt.instruction.value)) {
|
||||
for (const operand of eachReactiveValueOperand(stmt.instruction.value)) {
|
||||
scopes.visit(operand.identifier);
|
||||
}
|
||||
if (stmt.instruction.lvalue !== null) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ReactiveInstruction,
|
||||
ReactiveScope,
|
||||
ReactiveTerminal,
|
||||
ReactiveValue,
|
||||
ReactiveValueBlock,
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
@@ -33,7 +34,7 @@ export function visitFunction(
|
||||
switch (item.kind) {
|
||||
case "instruction": {
|
||||
if (visitValue) {
|
||||
for (const operand of eachInstructionValueOperand(
|
||||
for (const operand of eachReactiveValueOperand(
|
||||
item.instruction.value
|
||||
)) {
|
||||
visitValue(operand);
|
||||
@@ -81,6 +82,21 @@ export function visitFunction(
|
||||
visitBlock(fn.body);
|
||||
}
|
||||
|
||||
export function* eachReactiveValueOperand(
|
||||
instrValue: ReactiveValue
|
||||
): Iterable<Place> {
|
||||
switch (instrValue.kind) {
|
||||
case "LogicalExpression": {
|
||||
yield* eachReactiveValueOperand(instrValue.left);
|
||||
yield* eachReactiveValueOperand(instrValue.right);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
yield* eachInstructionValueOperand(instrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTerminalBlocks(
|
||||
terminal: ReactiveTerminal,
|
||||
fn: (block: ReactiveBlock) => ReactiveBlock
|
||||
|
||||
Reference in New Issue
Block a user