Compare commits
1 commit
main
...
gabritto/i
Author | SHA1 | Date | |
---|---|---|---|
7684d66ba2 |
|
@ -1433,15 +1433,15 @@ namespace ts {
|
|||
}
|
||||
|
||||
function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
|
||||
const preRightLabel = createBranchLabel();
|
||||
const preRightLabel = createBranchLabel(); // V
|
||||
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
|
||||
bindCondition(node.left, preRightLabel, falseTarget);
|
||||
}
|
||||
else {
|
||||
bindCondition(node.left, trueTarget, preRightLabel);
|
||||
}
|
||||
currentFlow = finishFlowLabel(preRightLabel);
|
||||
bind(node.operatorToken);
|
||||
currentFlow = finishFlowLabel(preRightLabel); // V
|
||||
bind(node.operatorToken); // V
|
||||
|
||||
if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) {
|
||||
doWithConditionalBranches(bind, node.right, trueTarget, falseTarget);
|
||||
|
@ -1498,11 +1498,21 @@ namespace ts {
|
|||
}
|
||||
|
||||
function createBindBinaryExpressionFlow() {
|
||||
interface LogicalLikeState {
|
||||
trueTarget: FlowLabel;
|
||||
falseTarget: FlowLabel;
|
||||
preRightLabel: FlowLabel;
|
||||
postExpressionLabel?: FlowLabel;
|
||||
}
|
||||
|
||||
interface WorkArea {
|
||||
stackIndex: number;
|
||||
skip: boolean;
|
||||
// skip: boolean;
|
||||
inStrictModeStack: (boolean | undefined)[];
|
||||
parentStack: (Node | undefined)[];
|
||||
currentBranchTargetStack: ([FlowLabel | undefined, FlowLabel | undefined])[];
|
||||
logicalStack: (LogicalLikeState | undefined)[];
|
||||
isLogicalLike: boolean;
|
||||
}
|
||||
|
||||
return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
|
||||
|
@ -1517,64 +1527,134 @@ namespace ts {
|
|||
bindWorker(node);
|
||||
const saveParent = parent;
|
||||
parent = node;
|
||||
state.skip = false;
|
||||
// state.skip = false;
|
||||
state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
|
||||
state.parentStack[state.stackIndex] = saveParent;
|
||||
state.currentBranchTargetStack[state.stackIndex] = [currentTrueTarget, currentFalseTarget];
|
||||
}
|
||||
else {
|
||||
state = {
|
||||
stackIndex: 0,
|
||||
skip: false,
|
||||
// skip: false,
|
||||
inStrictModeStack: [undefined],
|
||||
parentStack: [undefined]
|
||||
parentStack: [undefined],
|
||||
logicalStack: [undefined],
|
||||
currentBranchTargetStack: [[currentTrueTarget, currentFalseTarget]],
|
||||
isLogicalLike: false,
|
||||
};
|
||||
}
|
||||
// TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
|
||||
// we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
|
||||
// For now, though, since the common cases are chained `+`, leaving it recursive is fine
|
||||
|
||||
const operator = node.operatorToken.kind;
|
||||
if (operator === SyntaxKind.AmpersandAmpersandToken ||
|
||||
operator === SyntaxKind.BarBarToken ||
|
||||
operator === SyntaxKind.QuestionQuestionToken ||
|
||||
isLogicalOrCoalescingAssignmentOperator(operator)) {
|
||||
state.isLogicalLike = true;
|
||||
|
||||
let logicalState: LogicalLikeState;
|
||||
const preRightLabel = createBranchLabel();
|
||||
if (isTopLevelLogicalExpression(node)) {
|
||||
const postExpressionLabel = createBranchLabel();
|
||||
bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
const postExpressionLabel = createBranchLabel(); // >> TODO: we need to set current flow afterwards, at the end of onexit
|
||||
logicalState = {
|
||||
preRightLabel,
|
||||
trueTarget: postExpressionLabel,
|
||||
falseTarget: postExpressionLabel,
|
||||
postExpressionLabel,
|
||||
};
|
||||
// bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
||||
// currentFlow = finishFlowLabel(postExpressionLabel); // >> TODO: on exit?
|
||||
}
|
||||
else {
|
||||
bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
|
||||
logicalState = {
|
||||
preRightLabel,
|
||||
trueTarget: currentTrueTarget!,
|
||||
falseTarget: currentFalseTarget!,
|
||||
};
|
||||
}
|
||||
state.skip = true;
|
||||
state.logicalStack[state.stackIndex] = logicalState;
|
||||
}
|
||||
|
||||
// TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
|
||||
// we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
|
||||
// For now, though, since the common cases are chained `+`, leaving it recursive is fine // >> TODO: suspiscious
|
||||
// const operator = node.operatorToken.kind;
|
||||
// if (operator === SyntaxKind.AmpersandAmpersandToken ||
|
||||
// operator === SyntaxKind.BarBarToken ||
|
||||
// operator === SyntaxKind.QuestionQuestionToken ||
|
||||
// isLogicalOrCoalescingAssignmentOperator(operator)) {
|
||||
// if (isTopLevelLogicalExpression(node)) {
|
||||
// const postExpressionLabel = createBranchLabel();
|
||||
// bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
|
||||
// currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
// }
|
||||
// else {
|
||||
// bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
|
||||
// }
|
||||
// state.skip = true;
|
||||
// }
|
||||
return state;
|
||||
}
|
||||
|
||||
function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
|
||||
if (!state.skip) {
|
||||
return maybeBind(left);
|
||||
function onLeft(left: Expression, state: WorkArea, node: BinaryExpression) {
|
||||
if (state.isLogicalLike) {
|
||||
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
|
||||
node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
|
||||
currentTrueTarget = state.logicalStack[state.stackIndex]!.preRightLabel;
|
||||
currentFalseTarget = state.logicalStack[state.stackIndex]!.falseTarget;
|
||||
}
|
||||
else {
|
||||
currentTrueTarget = state.logicalStack[state.stackIndex]!.trueTarget;
|
||||
currentFalseTarget = state.logicalStack[state.stackIndex]!.preRightLabel;
|
||||
}
|
||||
}
|
||||
return maybeBind(left);
|
||||
// if (!state.skip) {
|
||||
// return maybeBind(left);
|
||||
// }
|
||||
}
|
||||
|
||||
function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
|
||||
if (!state.skip) {
|
||||
if (operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
if (state.isLogicalLike) {
|
||||
if (!isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
|
||||
addAntecedent(currentTrueTarget!, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
|
||||
addAntecedent(currentFalseTarget!, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
|
||||
}
|
||||
// >> TODO: restore current true and current false right here, to emulate end of `bindCondition` call
|
||||
currentFlow = finishFlowLabel(state.logicalStack[state.stackIndex]!.preRightLabel);
|
||||
}
|
||||
else if (operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
maybeBindExpressionFlowIfCall(node.left);
|
||||
}
|
||||
bind(operatorToken);
|
||||
}
|
||||
// if (!state.skip) {
|
||||
// if (operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
// maybeBindExpressionFlowIfCall(node.left);
|
||||
// }
|
||||
// bind(operatorToken);
|
||||
// }
|
||||
}
|
||||
|
||||
function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
|
||||
if (!state.skip) {
|
||||
return maybeBind(right);
|
||||
function onRight(right: Expression, state: WorkArea, node: BinaryExpression) {
|
||||
if (state.isLogicalLike) {
|
||||
// >> TODO: do special stuff if assignment operator
|
||||
currentTrueTarget = state.logicalStack[state.stackIndex]!.trueTarget;
|
||||
currentFalseTarget = state.logicalStack[state.stackIndex]!.falseTarget;
|
||||
}
|
||||
return maybeBind(right);
|
||||
// if (!state.skip) {
|
||||
// return maybeBind(right);
|
||||
// }
|
||||
}
|
||||
|
||||
function onExit(node: BinaryExpression, state: WorkArea) {
|
||||
if (!state.skip) {
|
||||
const operator = node.operatorToken.kind;
|
||||
if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
|
||||
if (state.isLogicalLike) {
|
||||
const postExpressionLabel = state.logicalStack[state.stackIndex]?.postExpressionLabel;
|
||||
if (postExpressionLabel) {
|
||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
}
|
||||
}
|
||||
else if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
|
||||
bindAssignmentTargetFlow(node.left);
|
||||
if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
|
||||
const elementAccess = node.left as ElementAccessExpression;
|
||||
|
@ -1583,7 +1663,6 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const savedInStrictMode = state.inStrictModeStack[state.stackIndex];
|
||||
const savedParent = state.parentStack[state.stackIndex];
|
||||
if (savedInStrictMode !== undefined) {
|
||||
|
@ -1592,7 +1671,9 @@ namespace ts {
|
|||
if (savedParent !== undefined) {
|
||||
parent = savedParent;
|
||||
}
|
||||
state.skip = false;
|
||||
currentTrueTarget = state.currentBranchTargetStack[state.stackIndex][0];
|
||||
currentFalseTarget = state.currentBranchTargetStack[state.stackIndex][1];
|
||||
state.isLogicalLike = false;
|
||||
state.stackIndex--;
|
||||
}
|
||||
|
||||
|
|
|
@ -1016,7 +1016,15 @@ namespace ts {
|
|||
* @param frame The current frame
|
||||
* @returns The new frame
|
||||
*/
|
||||
export function enter<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number {
|
||||
export function enter<TOuterState, TState, TResult>(
|
||||
machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>,
|
||||
stackIndex: number,
|
||||
stateStack: BinaryExpressionState[],
|
||||
nodeStack: BinaryExpression[],
|
||||
userStateStack: TState[],
|
||||
_resultHolder: { value: TResult },
|
||||
outerState: TOuterState,
|
||||
): number {
|
||||
const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined;
|
||||
Debug.assertEqual(stateStack[stackIndex], enter);
|
||||
userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState);
|
||||
|
@ -1030,7 +1038,15 @@ namespace ts {
|
|||
* @param frame The current frame
|
||||
* @returns The new frame
|
||||
*/
|
||||
export function left<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
|
||||
export function left<TOuterState, TState, TResult>(
|
||||
machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>,
|
||||
stackIndex: number,
|
||||
stateStack: BinaryExpressionState[],
|
||||
nodeStack: BinaryExpression[],
|
||||
userStateStack: TState[],
|
||||
_resultHolder: { value: TResult },
|
||||
_outerState: TOuterState,
|
||||
): number {
|
||||
Debug.assertEqual(stateStack[stackIndex], left);
|
||||
Debug.assertIsDefined(machine.onLeft);
|
||||
stateStack[stackIndex] = nextState(machine, left);
|
||||
|
|
1005
tests/cases/compiler/bigOrExpression.ts
Normal file
1005
tests/cases/compiler/bigOrExpression.ts
Normal file
File diff suppressed because it is too large
Load diff
1
tests/cases/compiler/bigOrExpressionTemp.ts
Normal file
1
tests/cases/compiler/bigOrExpressionTemp.ts
Normal file
|
@ -0,0 +1 @@
|
|||
const x = "a" + "b" + "c" + "d" + "e" + "f" + "g";
|
Loading…
Reference in a new issue