@@ -121,7 +121,7 @@ class ChainContext {
121121 * true go one way and if false go the other (tracked by `trueForkContext` and
122122 * `falseForkContext`). The `??` operator doesn't operate on true/false because
123123 * the left expression is evaluated to be nullish or not, so only if nullish do
124- * we fork to the right expression (tracked by `qqForkcontext `).
124+ * we fork to the right expression (tracked by `nullishForkContext `).
125125 */
126126class ChoiceContext {
127127
@@ -170,14 +170,14 @@ class ChoiceContext {
170170this . falseForkContext = ForkContext . newEmpty ( forkContext ) ;
171171
172172/**
173- * The fork context for theright side of the `??` path of the choice .
173+ * The fork context forwhen thechoice result is `null` or `undefined` .
174174 *@type {ForkContext }
175175 */
176- this . qqForkContext = ForkContext . newEmpty ( forkContext ) ;
176+ this . nullishForkContext = ForkContext . newEmpty ( forkContext ) ;
177177
178178/**
179179 * Indicates if any of `trueForkContext`, `falseForkContext`, or
180- * `qqForkContext ` have been updated with segments from a child context.
180+ * `nullishForkContext ` have been updated with segments from a child context.
181181 *@type {boolean }
182182 */
183183this . processed = false ;
@@ -849,7 +849,7 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
849849if ( ! choiceContext . processed ) {
850850choiceContext . trueForkContext . add ( head ) ;
851851choiceContext . falseForkContext . add ( head ) ;
852- choiceContext . qqForkContext . add ( head ) ;
852+ choiceContext . nullishForkContext . add ( head ) ;
853853}
854854
855855/*
@@ -1095,31 +1095,31 @@ class CodePathState {
10951095
10961096/**
10971097 * Pops the last choice context and finalizes it.
1098+ * This is called upon leaving a node that represents a choice.
10981099 *@throws {Error } (Unreachable.)
10991100 *@returns {ChoiceContext } The popped context.
11001101 */
11011102popChoiceContext ( ) {
1102- const context = this . choiceContext ;
1103-
1104- this . choiceContext = context . upper ;
1105-
1103+ const poppedChoiceContext = this . choiceContext ;
11061104const forkContext = this . forkContext ;
1107- const headSegments = forkContext . head ;
1105+ const head = forkContext . head ;
11081106
1109- switch ( context . kind ) {
1107+ this . choiceContext = poppedChoiceContext . upper ;
1108+
1109+ switch ( poppedChoiceContext . kind ) {
11101110case "&&" :
11111111case "||" :
11121112case "??" :
11131113
11141114/*
1115- * The `headSegments ` are the path of the right-hand operand.
1115+ * The `head ` are the path of the right-hand operand.
11161116 * If we haven't previously added segments from child contexts,
11171117 * then we add these segments to all possible forks.
11181118 */
1119- if ( ! context . processed ) {
1120- context . trueForkContext . add ( headSegments ) ;
1121- context . falseForkContext . add ( headSegments ) ;
1122- context . qqForkContext . add ( headSegments ) ;
1119+ if ( ! poppedChoiceContext . processed ) {
1120+ poppedChoiceContext . trueForkContext . add ( head ) ;
1121+ poppedChoiceContext . falseForkContext . add ( head ) ;
1122+ poppedChoiceContext . nullishForkContext . add ( head ) ;
11231123}
11241124
11251125/*
@@ -1128,38 +1128,38 @@ class CodePathState {
11281128 * then we take the segments for this context and move them up
11291129 * to the parent context.
11301130 */
1131- if ( context . isForkingAsResult ) {
1131+ if ( poppedChoiceContext . isForkingAsResult ) {
11321132const parentContext = this . choiceContext ;
11331133
1134- parentContext . trueForkContext . addAll ( context . trueForkContext ) ;
1135- parentContext . falseForkContext . addAll ( context . falseForkContext ) ;
1136- parentContext . qqForkContext . addAll ( context . qqForkContext ) ;
1134+ parentContext . trueForkContext . addAll ( poppedChoiceContext . trueForkContext ) ;
1135+ parentContext . falseForkContext . addAll ( poppedChoiceContext . falseForkContext ) ;
1136+ parentContext . nullishForkContext . addAll ( poppedChoiceContext . nullishForkContext ) ;
11371137parentContext . processed = true ;
11381138
11391139// Exit early so we don't collapse all paths into one.
1140- return context ;
1140+ return poppedChoiceContext ;
11411141}
11421142
11431143break ;
11441144
11451145case "test" :
1146- if ( ! context . processed ) {
1146+ if ( ! poppedChoiceContext . processed ) {
11471147
11481148/*
11491149 * The head segments are the path of the `if` block here.
11501150 * Updates the `true` path with the end of the `if` block.
11511151 */
1152- context . trueForkContext . clear ( ) ;
1153- context . trueForkContext . add ( headSegments ) ;
1152+ poppedChoiceContext . trueForkContext . clear ( ) ;
1153+ poppedChoiceContext . trueForkContext . add ( head ) ;
11541154} else {
11551155
11561156/*
11571157 * The head segments are the path of the `else` block here.
11581158 * Updates the `false` path with the end of the `else`
11591159 * block.
11601160 */
1161- context . falseForkContext . clear ( ) ;
1162- context . falseForkContext . add ( headSegments ) ;
1161+ poppedChoiceContext . falseForkContext . clear ( ) ;
1162+ poppedChoiceContext . falseForkContext . add ( head ) ;
11631163}
11641164
11651165break ;
@@ -1170,7 +1170,7 @@ class CodePathState {
11701170 * Loops are addressed in `popLoopContext()` so just return
11711171 * the context without modification.
11721172 */
1173- return context ;
1173+ return poppedChoiceContext ;
11741174
11751175/* c8 ignore next */
11761176default :
@@ -1180,71 +1180,116 @@ class CodePathState {
11801180/*
11811181 * Merge the true path with the false path to create a single path.
11821182 */
1183- const combinedForkContext = context . trueForkContext ;
1183+ const combinedForkContext = poppedChoiceContext . trueForkContext ;
11841184
1185- combinedForkContext . addAll ( context . falseForkContext ) ;
1185+ combinedForkContext . addAll ( poppedChoiceContext . falseForkContext ) ;
11861186forkContext . replaceHead ( combinedForkContext . makeNext ( 0 , - 1 ) ) ;
11871187
1188- return context ;
1188+ return poppedChoiceContext ;
11891189}
11901190
11911191/**
1192- *Makes a code path segmentof the right-hand operand of a logical
1192+ *Creates a code path segmentto represent right-hand operand of a logical
11931193 * expression.
1194+ * This is called in the preprocessing phase when entering a node.
11941195 *@throws {Error } (Unreachable.)
11951196 *@returns {void }
11961197 */
11971198makeLogicalRight ( ) {
1198- const context = this . choiceContext ;
1199+ const currentChoiceContext = this . choiceContext ;
11991200const forkContext = this . forkContext ;
12001201
1201- if ( context . processed ) {
1202+ if ( currentChoiceContext . processed ) {
12021203
12031204/*
1204- * This got segments already from the child choice context.
1205- * Creates the next path from own true/false fork context.
1205+ * This context was already assigned segments from a child
1206+ * choice context. In this case, we are concerned only about
1207+ * the path that does not short-circuit and so ends up on the
1208+ * right-hand operand of the logical expression.
12061209 */
12071210let prevForkContext ;
12081211
1209- switch ( context . kind ) {
1212+ switch ( currentChoiceContext . kind ) {
12101213case "&&" :// if true then go to the right-hand side.
1211- prevForkContext = context . trueForkContext ;
1214+ prevForkContext = currentChoiceContext . trueForkContext ;
12121215break ;
12131216case "||" :// if false then go to the right-hand side.
1214- prevForkContext = context . falseForkContext ;
1217+ prevForkContext = currentChoiceContext . falseForkContext ;
12151218break ;
1216- case "??" :// Both true/false can short-circuit, so needs the third path to go to the right-hand side. That'sqqForkContext .
1217- prevForkContext = context . qqForkContext ;
1219+ case "??" :// Both true/false can short-circuit, so needs the third path to go to the right-hand side. That'snullishForkContext .
1220+ prevForkContext = currentChoiceContext . nullishForkContext ;
12181221break ;
12191222default :
12201223throw new Error ( "unreachable" ) ;
12211224}
12221225
1226+ /*
1227+ * Create the segment for the right-hand operand of the logical expression
1228+ * and adjust the fork context pointer to point there. The right-hand segment
1229+ * is added at the end of all segments in `prevForkContext`.
1230+ */
12231231forkContext . replaceHead ( prevForkContext . makeNext ( 0 , - 1 ) ) ;
1232+
1233+ /*
1234+ * We no longer need this list of segments.
1235+ *
1236+ * Reset `processed` because we've removed the segments from the child
1237+ * choice context. This allows `popChoiceContext()` to continue adding
1238+ * segments later.
1239+ */
12241240prevForkContext . clear ( ) ;
1225- context . processed = false ;
1241+ currentChoiceContext . processed = false ;
1242+
12261243} else {
12271244
12281245/*
1229- * This did not get segments from the child choice context.
1230- * So addresses the head segments.
1231- * The head segments are the path of the left-hand operand.
1246+ * This choice context was not assigned segments from a child
1247+ * choice context, which means that it's a terminal logical
1248+ * expression.
1249+ *
1250+ * `head` is the segments for the left-hand operand of the
1251+ * logical expression.
1252+ *
1253+ * Each of the fork contexts below are empty at this point. We choose
1254+ * the path(s) that will short-circuit and add the segment for the
1255+ * left-hand operand to it. Ultimately, this will be the only segment
1256+ * in that path due to the short-circuting, so we are just seeding
1257+ * these paths to start.
12321258 */
1233- switch ( context . kind ) {
1234- case "&&" :// the false path can short-circuit.
1235- context . falseForkContext . add ( forkContext . head ) ;
1259+ switch ( currentChoiceContext . kind ) {
1260+ case "&&" :
1261+
1262+ /*
1263+ * In most contexts, when a && expression evaluates to false,
1264+ * it short circuits, so we need to account for that by setting
1265+ * the `falseForkContext` to the left operand.
1266+ *
1267+ * When a && expression is the left-hand operand for a ??
1268+ * expression, such as `(a && b) ?? c`, a nullish value will
1269+ * also short-circuit in a different way than a false value,
1270+ * so we also set the `nullishForkContext` to the left operand.
1271+ * This path is only used with a ?? expression and is thrown
1272+ * away for any other type of logical expression, so it's safe
1273+ * to always add.
1274+ */
1275+ currentChoiceContext . falseForkContext . add ( forkContext . head ) ;
1276+ currentChoiceContext . nullishForkContext . add ( forkContext . head ) ;
12361277break ;
12371278case "||" :// the true path can short-circuit.
1238- context . trueForkContext . add ( forkContext . head ) ;
1279+ currentChoiceContext . trueForkContext . add ( forkContext . head ) ;
12391280break ;
12401281case "??" :// both can short-circuit.
1241- context . trueForkContext . add ( forkContext . head ) ;
1242- context . falseForkContext . add ( forkContext . head ) ;
1282+ currentChoiceContext . trueForkContext . add ( forkContext . head ) ;
1283+ currentChoiceContext . falseForkContext . add ( forkContext . head ) ;
12431284break ;
12441285default :
12451286throw new Error ( "unreachable" ) ;
12461287}
12471288
1289+ /*
1290+ * Create the segment for the right-hand operand of the logical expression
1291+ * and adjust the fork context pointer to point there.
1292+ */
12481293forkContext . replaceHead ( forkContext . makeNext ( - 1 , - 1 ) ) ;
12491294}
12501295}
@@ -1265,7 +1310,7 @@ class CodePathState {
12651310if ( ! context . processed ) {
12661311context . trueForkContext . add ( forkContext . head ) ;
12671312context . falseForkContext . add ( forkContext . head ) ;
1268- context . qqForkContext . add ( forkContext . head ) ;
1313+ context . nullishForkContext . add ( forkContext . head ) ;
12691314}
12701315
12711316context . processed = false ;