-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
Disallow duplicated nodes (only in tests output) #7149
Conversation
@@ -68,7 +68,7 @@ function _foo() { | |||
while (1) { | |||
switch (_context3.prev = _context3.next) { | |||
case 0: | |||
_bar2 = function _bar2() { | |||
_bar2 = function _ref2() { | |||
_bar2 = _asyncToGenerator( |
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 that this was a bug, since _bar2
wasn't actually reassigned (because the variable was shadowed by the function)
@@ -172,6 +322,7 @@ function run(task) { | |||
if (execCode) { | |||
const execOpts = getOpts(exec); | |||
result = babel.transform(execCode, execOpts); | |||
checkDuplicatedNodes(result.ast); |
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 guess this might create quite some overhead, ideally we should not do this by default, maybe only on CI?
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 will disable it on normal make test
at the end of the pr, when the tests are green.
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 it actually slower?
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.
With the check:
make test-only 60,29s user 4,13s system 115% cpu 55,727 total
Without:
make test-only 57,88s user 3,98s system 115% cpu 53,697 total
We can run the check always.
if (isByRegenerator(node)) return; | ||
if (nodes.has(node)) { | ||
throw new Error( | ||
"Do not reuse nodes. Use `t.clone` or `t.cloneDeep` to copy them.\n" + |
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.
(Q: when to not use cloneDeep?)
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.
When you are working with a literal, which doesn't have children
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.
But if you are working with a literal, cloneDeep just won't find additional objects to recurse and so it won't recurse. Any reason why we can't just provide a single interface to reduce errors? Would the performance impact of the extra typeof check be too great?
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 can make clone
behave as cloneDeep
and special-case literals to avoid looping over their properties.
353 tests failing at the moment 🎉 |
@Andarist @Kovensky What do you think of Also, I think we used somewhere clone instead of cloneDeep, because the number of failing tests decreased by 2 🎉 |
We should add |
EDIT: oh, you added it on a commit; time to read it |
if (has(node, field)) { | ||
newNode[field] = isNode(node[field]) | ||
? cloneNode(node[field]) | ||
: node[field]; |
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 won't clone nodes that are inside a statement list (BlockStatement
children, etc)
@@ -0,0 +1,35 @@ | |||
import { NODE_FIELDS } from "../definitions"; | |||
|
|||
const has = Function.call.bind(Object.prototype.hasOwnProperty); |
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.
(call
is technically in Function.prototype
but it works I guess)
type: node.type, | ||
}: any): T); | ||
for (const field in NODE_FIELDS[node.type]) { | ||
if (has(node, field)) { |
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 tell you to use Object.keys
but I assume you did this for speed
const isNode = obj => obj && has(NODE_FIELDS, obj.type); | ||
|
||
export default function cloneNode<T: Object>(node: T): T { | ||
if (!node) return node; |
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.
If T
extends Object
it can't be falsy, unless it's document.all
(I guess this is defensive programming)
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.
Oh lol, I copied lines 6-7 from clone
. I will leave the check here, because plug-in authors might not use Flow.
const has = Function.call.bind(Object.prototype.hasOwnProperty); | ||
const isNode = obj => obj && has(NODE_FIELDS, obj.type); | ||
|
||
export default function cloneNode<T: Object>(node: T): T { |
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 say it would be better to extend BabelNode
instead...... but actually reading the BabelNode
definition and how it's used makes it a bit doubtful.
For example, in TypeScript, almost every valid node (written as an object literal) you gave it would get caught in excess property checks for weak types. It'd probably be better to make BabelNode
be a generic, where the generic argument is the node type literal string.
But I digress and is outside the scope of this change.
if (!node) return node; | ||
|
||
// Special-case identifiers since they are the most cloned nodes. | ||
if (node.type === "Identifier") { |
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 better to save type
to a local var (const { type } = node
) since you reuse it at least 3 times
} | ||
} | ||
|
||
newNode.loc = node.loc || null; |
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.
Since you're now specifically only copying known AST keys, this is missing comments.
@nicolo-ribaudo do you plan to remove |
Remove or deprecate with a nice warning message |
If you gonna deprecate them, which I guess is fine, considering scope of the babel and its users count, I'd propose to have them just proxy to I must also add, that I really like this change - was really hard to determine which one ( |
Done 👍 In the test output there is the depecation message twice, because regenerator uses |
24 failing tests 🎉 |
Zero tests failing locally 🎉 @Andarist I'll rebase this commits to resolve the merge conflicts and squash them. Are you ok if I squash them like this?
|
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/6739/ |
d1a329a
to
def83a3
Compare
def83a3
to
79ef356
Compare
I've gone ahead and squashed. The original commits are at https://github.com/nicolo-ribaudo/babel/tree/duplicated-nodes-old. There were so many conflicts when reordering commits that I don't know how I managed not to break everything* 😆 * On my PC there is |
Let's try to land this asap? @loganfsmyth thoughts? |
a02297f
to
c7a2d35
Compare
When merging, please don't squash to keep both @Andarist and I as the commit authors. |
You can merge if it's good now 👍 |
I'm waiting for travis and circle ci to finish 👍 |
wohoooo 🎉 |
amazing - now we should figure out how to make this a thing in all the 3rd party plugins with our plugin cli tool 😄 |
Unfortunately this PR won't be easy to review 🙁
Anyway, currently there are 604 failing tests 😝
I had to add some logic do skip validating nodes generated by regenerator-transform, since it isn't in this repository and it duplicates a lot of nodes.
cc @Andarist @existentialism
Closes #6375