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
Do not tree-shake children of unknown nodes #2510
Conversation
anyone willing to merge this? |
Sorry for letting you wait as I was quite occupied with personal things for the last weeks. |
@lukastaegert for the following input: px = do {
const _hygenicTemp = ToPrimitive(x, 'Number');
if (_hygenicTemp instanceof AbruptCompletion) {
return _hygenicTemp;
} else if (_hygenicTemp instanceof Completion) {
_hygenicTemp.Value;
} else {
_hygenicTemp;
}
}; without this change it becomes px = do {
const _hygenicTemp = ToPrimitive$$1(x, 'Number');
if (_hygenicTemp instanceof AbruptCompletion) {
return _hygenicTemp;
} else if (_hygenicTemp instanceof Completion) {
_hygenicTemp.Value; // not removed because it could be an accessor
} // final `else` block has been pruned
}; applying this change stops this from happening. I would be happy to add a test if you can explain the layout of your tests, as its an actual labyrinth. |
Which acorn plugin are you using? With this plugin, we could actually turn this into a test. I am very hesitant to merge new code without tests because it is very easy to break this again. I still wonder why your change would fix this, though. The code inside the do expression looks like normal JavaScript so why would an identifier-as-expression-statement suddenly have an effect 🤔 |
@lukastaegert it is the same as how so in a do expression: const x = do {
if (condition) {
return 5; // this returns the function containing this do expression. x is not set to 5
} else if (condition2) {
'hi'; // x will be 'hi'
} else {
'bye'; // x will be 'bye'
}
}; my config looks like this right now: acorn: {
plugins: { doExpressions: true },
},
acornInjectPlugins: [(acorn) => {
acorn.plugins.doExpressions = function doExpressions(instance) {
instance.extend('parseExprAtom', (superF) => function parseExprAtom(...args) {
if (this.type === acorn.tokTypes._do) { // eslint-disable-line no-underscore-dangle
this.eat(acorn.tokTypes._do); // eslint-disable-line no-underscore-dangle
const node = this.startNode();
node.body = this.parseBlock();
return this.finishNode(node, 'DoExpression');
}
return Reflect.apply(superF, this, args);
});
};
return acorn;
}], |
but the point here is that we don't know what magical things future versions of js will do, so it's unsafe to perform anything on the contents of unknown nodes. |
Sure, but you will also understand that instead of adding potentially dead code that "looks right", I strive to add code that is proven to be right, also considering that at the moment I am more or less doing by far most of the maintenance of rollup alone. I started to look into writing a test but as I suspected, this fix does not seem to solve your dilemma. The generated AST structure of a do expression is this:
so the do expression just contains a plain block statement which means the usual tree-shaking logic of a block statement applies. How I understand the semantic of do expressions, we would need to treat the last statement of any possible execution branch inside the block statement the same way we would treat a return expression. For that again, we would need some kind of special flag that we are inside a do expression to apply that special logic. As this is still a stage 1 feature, I would not feel confident adding such deep changes to rollup but rather hope that people who want to use do expressions would turn to babel for now. Nevertheless, I will check if we cannot add a generic test for a generic acorn plugin to validate the added logic. |
@lukastaegert the changes i made seemed to work locally for me, but i might have done something else, as i'm not familiar at all with this codebase. whatever changes need to be made should be made. children of unknown node types should not be subject to tree shaking. because that BlockStatement belongs to a node rollup doesn't understand, it should ignore it. |
Now this is something I can get behind. For now, I have added a test to your branch checking for any kind of undetected side-effects of unknown expressions that do not have children and as I suspected, all was already working correctly. But the latter is definitely correct and would solve your issue. I will not have more time today but I hope can look into this tomorrow. Until then if you can add a test for this (take my test as inspiration), that would be cool! If you have ideas how to solve the entire issue, you are of course welcome as well! |
@lukastaegert added new test case and its passing now 👍 |
Unknown nodes may have any behaviour, as they are unknown. It is therefore unsafe to assume the unknown node, or any child of the unknown node, is able to be removed from the render. Fixes #2506
Very good! Unfortunately, cutting off all logic for unknown nodes has some unintended side-effects, namely: Inside the do-expression, there is still "normal" JavaScript which may still use variables outside the expression. These variables will not be properly included if the logic is suppressed. I modified the test to reflects this (and changed the existing test cases to better reflect how we expect do expressions to work as I believe this makes them more readable). I fear this will require a little more work but I am on it now. In essence, all child nodes need to be included using the "proper" inclusion logic. I will check if we can manage this via a flag. |
I implemented a more comprehensive fix now that also refactors the To make sure we do not forget this when extending the tree-shaking logic I made the new flag mandatory, which should also be optimized better by the JS runtime. Please have a look, if this works for you then I would merge this. |
merge at your convenience |
This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
#2506
Description
Unknown nodes may have any behaviour, as they are unknown. It is
therefore unsafe to assume the unknown node, or any child of the unknown
node, is able to be removed from the render.