-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Nullish Coalescing operator #6545
Conversation
This operator is similar to the logical or operator (`||`) but is meant to avoid unintended behavior which might arise when using logical or. Problem with logical or is that the left argument is coerced to a boolean which means that if it is something falsy (like `0`, `''`, or `false`) then the right argument will be used instead. The coalescing operator (`??`) is designed to solve this problem. It only assigns the right value when the left value is `null` or `undefined`. ```javascript null || 'something'; // 'something' undefined || 'something'; // 'something' '' || 'something'; // 'something' 0 || 'something'; // 'something' false || 'something'; // 'something' null ?? 'something'; // 'something' undefined ?? 'something'; // 'something' '' ?? 'something'; // '' 0 ?? 'something'; // 0 false ?? 'something'; // false ```
@@ -279,6 +279,7 @@ LPCWSTR Parser::GetTokenString(tokens token) | |||
case tkColon: return _u(":"); | |||
case tkLogOr: return _u("||"); | |||
case tkLogAnd: return _u("&&"); | |||
case tkCoalesce: return _u("??"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This switch converts parser tokens back into strings for error messages.
@@ -3237,7 +3238,8 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall, | |||
_Out_opt_ BOOL* pfCanAssign /*= nullptr*/, | |||
_Inout_opt_ BOOL* pfLikelyPattern /*= nullptr*/, | |||
_Out_opt_ bool* pfIsDotOrIndex /*= nullptr*/, | |||
_Inout_opt_ charcount_t *plastRParen /*= nullptr*/) | |||
_Inout_opt_ charcount_t *plastRParen /*= nullptr*/, | |||
_Out_opt_ bool* looseCoalesce /*= nullptr*/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The looseCoalesce out parameter enables tracking of unbracketed coalescing operators for the syntax error when they are combined with ?? or ||.
|
||
if (nop == knopLogAnd || nop == knopLogOr) | ||
{ | ||
if (localCoalesce || coalescing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the check for the syntax error - localCoalesce
will be true if the left hand side of this operation is an unbracketed ??, coalescing
will be true if the right hand side is.
Aware the variable names aren't the best - open to alternatives.
localCoalesce = true; | ||
if (looseCoalesce != nullptr) | ||
{ | ||
*looseCoalesce = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we mark that the current operation is a coalescing operation to feed up into the above check at a different level.
@@ -3440,6 +3442,11 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall, | |||
|
|||
ChkCurTok(tkRParen, ERRnoRparen); | |||
|
|||
if (looseCoalesce != nullptr) | |||
{ | |||
*looseCoalesce = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last passed statement was wrapped in brackets - therefore it is not a "loose" coalesce.
LSC_ERROR_MSG(1075, ERRInvalidAssignmentTarget, "Invalid destructuring assignment target") | ||
LSC_ERROR_MSG(1076, ERRFormalSame, "Duplicate formal parameter names not allowed in this context") | ||
LSC_ERROR_MSG(1077, ERRDestructNotInit, "Destructuring declarations cannot have an initializer") | ||
LSC_ERROR_MSG(1078, ERRCoalesce, "Coalescing operator '\?\?' not permitted unparenthesized inside '||' or '&&' expressions") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New error message for the new syntax error. The changes above in this file are just white space.
// In that case the right hand side is not evaluated | ||
// If the left hand side is null or undefined it resolves to the right hand side | ||
// PTNODE(knopCoalesce , "??" ,None ,Bin ,fnopBin) | ||
case knopCoalesce: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case in the bytecode emitter is the run time implementation - the command is fully de-sugared here, so no new opcodes were needed.
Word of warning that per the DOM spec, |
Falsely shouldn't be a problem - only if tests with |
Yeah, oddly enough I don’t think it’s a major issue in practice, like you said. Just wanted to state it for the record since embedders have no way to emulate this behavior. |
Doing the usual clopenning to clear persistent CI failure. |
Thinking about it - if any browser does embed this and if a developer wants to do |
It turns out that the special case under discussion is actually formally encoded in the ES spec, see https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot |
Yeah I read it - and I don't think it's worth supporting for now, to handle it in this operation could either: (a) would be a lot of extra code and (b) would be a performance hit - if anyone actually makes a new webbrowser with CC and wants spec perfect support for window.all we can revisit. |
Alright, that makes sense. Just wanted to make sure everything was on the table first before a decision was made. I’ll duck out now 🦆 |
Closing and reopening in hopes to clear the CI failure. CC @divmain ;) |
@ppenzin interestingly there's an option to override the CI and merge - I'm guessing there's no way to retrigger it now the repository has been transferred - considering it passed everything other than windows 7 - think we should just merge it? |
@rhuanjl agree - we should merge it. |
This PR takes work begun by @boingoing in #6254 and completes it to implement the stage 4 tc39 proposal for a nullish coalescing operator.
Proposal repository: https://github.com/tc39/proposal-nullish-coalescing
The implementations passes most relevant test262 test cases - exception being ones also involving tail call optimisations which is a separate JS feature that CC does not support.
A switch
ESNullishCoalescingOperator
is included but set to true by default.The basic logic of this implementation is fairly simple - though one key complexity is that
a ?? b || c
is not permitted without brackets around one of the two operations, this required some extra tracking in the parser.Within the bytecode emitter the noalish coalescer is de-sugered so that:
result = node1 ?? node2;
becomes
@ppenzin @Fly-Style please could one of you take a look?