-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
WIP - Do expressions transform for switch statements #6975
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
Conversation
rajasekarm
commented
Dec 5, 2017
Q | A |
---|---|
Fixed Issues? | #3872 |
Patch: Bug Fix? | |
Major: Breaking Change? | |
Minor: New Feature? | |
Tests Added + Pass? | Yes |
Documentation PR | |
Any Dependency Changes? | |
License | MIT |
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/6170/ |
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.
Great job 👍
I was wondering why we needed to change babel-traverse for that but it's because the do-expression uses our internal
export function replaceExpressionWithStatements(nodes: Array<Object>) { |
); | ||
} else { | ||
const finalStatement = cases[i].get("consequent")[consequentLength - 1]; | ||
const isBreakStatement = |
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.
hasBreakStatement
would suit here better imho
paths, | ||
); | ||
} else { | ||
const finalStatement = cases[i].get("consequent")[consequentLength - 1]; |
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.
maybe something like lastCaseStatement
?
paths, | ||
); | ||
} else { | ||
const finalStatement = cases[i].get("consequent")[consequentLength - 1]; |
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.
we should also check if consequentLength - 1
is a valid index, accessing -1
may cause deoptimizations in v8
function completionRecordForSwitch(cases, paths) { | ||
for (let i = 0, caseLen = cases.length; i < caseLen; i++) { | ||
const consequentLength = cases[i].get("consequent").length; | ||
const isDefaultStatement = !cases[i].get("test").type; |
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.
a more explicit check of cases[i].get("test").type === null
could be used
also isDefaultCase
would be more descriptive
@@ -17,6 +17,38 @@ function addCompletionRecords(path, paths) { | |||
return paths; | |||
} | |||
|
|||
function completionRecordForSwitch(cases, paths) { | |||
for (let i = 0, caseLen = cases.length; i < caseLen; i++) { |
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.
is there any reason to cache caseLen
here?
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 would prefer to cache the array length always. Your views please.
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 should be a for (const case of cases) {}
loop. You make no use of the loop variables other than cases[i]
.
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.
changed to for of
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.
var a = do {
switch(0) {
case 0: "foo";
case 1: break;
}
} || "bar"
var b = do {
switch(0) {
case 0: break;
case 1: "foo"; break;
}
} || "bar"
alert(a + " " + b);
Expected output: foo bar
Actual output: bar foo
@nicolo-ribaudo @Andarist I'll update the PR and keep posted here. |
const isDefaultCase = switchcase.get("test").type === null; | ||
if (isDefaultCase) { | ||
paths = addCompletionRecords( | ||
switchcase.get("consequent")[consequentLength - 1], |
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 code path is not guarded against accessing -1 index
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.
updated the code to break the loop, if it has zero element
const lastCaseStatement = switchcase.get("consequent")[ | ||
consequentLength - 1 | ||
]; | ||
const hasBreakStatement = |
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.
as latter IfStatements are both checking against hasBreakStatement
you could just continue here for !hasBreakStatement
and remove those checks below
also the mentioned IfStatements should form if/else imho
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.
Done
I forgot about something @nicolo-ribaudo's example should become an |
I've few doubts, after @nicolo-ribaudo comment with the below example, Example 1:
Example 2:
Expected output: I read the the spec of switch case,, so as per spec the code should be transformed as below to get the proper evaluated output.
but it create confusion if there is change in structure of source code. Whats is the possible/right solution? |
That transformation wouldn't work, since var a = function() {
switch(0) {
case 0: return "foo";
case 1: return;
}
}() || "bar" If a case clause's first statement is I think you may need to loop over the case declarations from last to first, otherwise the |
Your exampleI simplified a bit your example: REPL (removed the Actual: abar foo Note that:var a = function () {
switch (0) {
case 0:
"foo";
}
}(); returns |
@xtuc but as per ECMA spec, last expression in the case block should be returned if there is no break statement. |
const hasBreakStatement = | ||
lastCaseStatement && lastCaseStatement.isBreakStatement(); | ||
|
||
if (hasBreakStatement) { |
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.
All these checks are the same of lines 62-80?
); | ||
} | ||
|
||
if (!hasBreakStatement && consequentLength) { |
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.
Only if the first statement of the following case
is break.
e.g.
let a = do {
switch(0) {
case 0:
"foo";
case 1:
"bar";
break;
}
};
Should be "bar".
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.
Yes right.
@rajzshkr so the behavior I explained here #6975 (comment) is a bug, do we agree? |
@xtuc Yup. The example in the comment should output as |
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.
Consequent could be not just ExpressionStatement, but BlockStatement. Inside this block, we can have break
, which should return the value above.
switch(0) {
case 0: {
0;
break;
}
case 1: "foo"; break;
}
for now, will return undefined
, because we didn't override break for this case and output is:
case 0: {
0;
break;
}
@yavorsky I've noted this issue also, I'll update the PR. |
@rajzshkr Will you update this recently? |
Any one can pick his progress? |