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
Optional chaining #6973
base: master
Are you sure you want to change the base?
Optional chaining #6973
Conversation
f4d884a
to
cc54764
Compare
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.
I think this looks neater than my version was, though struggling to follow how it handles every branch. What happens if you do a function call in an optional chain does it crash?
(Not going to merge until we have some significant test coverage)
I'm currently working on function calls. Edit: Function call should work now but |
No copy of expression result needed anymore
// ToDo: How can I run async tests? | ||
// simpleObj.nothing?.[await Promise.reject()]; | ||
// simpleObj.null?.[await Promise.reject()]; | ||
// simpleObj.undefined?.[await Promise.reject()]; |
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.
How can I run async tests?
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.
There are a few ways, have a look at test/es7/async-generator-functionality.js for one example.
Note, I'd recommend putting the async tests in a separate file - I think it's easier/better to manage them separately.
The jitted code currently crashes at this assertion (causing the test-failures) ChakraCore/lib/Backend/GlobOpt.cpp Line 11384 in 1f6e17c
|
aae4d9f
to
a9a1c70
Compare
b132685
to
9904051
Compare
@@ -70,7 +70,7 @@ static void EmitOptionalChainWrapper(ParseNodeUni *pnodeOptChain, ByteCodeGenera | |||
// Acquire slot for the result value | |||
Js::RegSlot resultSlot = funcInfo->AcquireLoc(pnodeOptChain); | |||
// Copy chain result | |||
byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A_ReuseLoc, resultSlot, innerNode->location); | |||
byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, resultSlot, innerNode->location); |
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.
Minor but I think you can delete this line.
resultSlot should be guaranteed to be the same register as innerNode->location here; I tested offline removing this line and your tests all still pass - except for the existing fail for the Simple method Calls test in the Jit.
May be good to replace with a comment noting this or an: Assert (innerNode->location == resultSlot);
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.
There's an edge case:
ByteCodeGenerator::ReturnRegister == resultSlot
=> innerNode->location != resultSlot
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.
Ugh didn't think of that. I suppose for optimal could do: if (innerNode->location != resultSlot)
and add the LdA or even a specific check for resultSlot == ByteCodeGenerator::ReturnRegister
Should include a test of this.
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.
I'd just go back to sth like
innerNode->location = funcInfo->AcquireLoc(pnodeOptChain);
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.
Question: Is it bad to reserve a register BEFORE emitting the whole chain-expression?
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.
I thought in the version before you did funcInfo->ReleaseLoc(innerNode)
but didn't actually do AcquireLoc for it as it happened inside the emitting of the chain.
I'm sorry this has brought in quite a bit of confusion, and I'm sorry I didn't think of the case where the resultSlot was already allocated (e.g. as the return reg) - I think it may be cleanest to return to the logic before but simply include an if(resultSlot != innerNode->location)
around the LdA.
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.
Ooh, no need to say sorry; I just did not understand 😅
After some testing I actually think we need
innerNode->location = funcInfo->AcquireLoc(pnodeOptChain);
though, because the stack of temp-registers will be asymmetrical released for optional-chains as call-target (another edge-case)
(a?.b)().c
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.
During testing, test262 sometimes failed while my own tests passed -> I need more tests!
(See ToDo in PR description)
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.
Thinking of extra tests, do you have a cases for an optional call where the property exists but is not callable?
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.
Now I have 😉
d8841a9
to
1a46f30
Compare
1a46f30
to
8baa9e6
Compare
5958fb4
to
8c4eddf
Compare
This PR aims to add support for the stage-4 proposal optional-chaining.
It's inspired by the work of @rhuanjl but uses a more hacky approach to parsing.
Goals
ToDo
eval?.('a')
)eval
)delete
operator is currently not workingWe might want to split this into a separat PR
this
(a.b)().c
should be equivalent toa.b().c
apply
call optimization?a?.[++x]
++x
should not be evaluated ifa
isnull
orundefined
a?.3:0
(ternary) astkOptChain
(?.
)Out of scope
EmitInvoke
(seems unused)Spec: Optional Chains
Fixes #6349