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
Add unsafe optimization to inline undefined for missing objlit props #589
base: master
Are you sure you want to change the base?
Conversation
This is a pretty good patch! However I believe it might not be the best solution for your specific use case. Maybe instead we can improve the existing I need to give more thought to this, but interesting ways to do this would be to use a label: |
Using Object.seal is clever, but it actually doesn't address the underlying unsafety - not only because it assumed Object.seal is unpatched, but also because sealing doesn't actually protect against prototype mutation. Another potential concern is scalability/composability - requiring all properties be defined in a single argument makes it harder to combine definitions from several different sources (that said, my approach introduces an unfortunate order dependence, so I'm definitely not sold on that, either). Please give it a think. |
The Another interesting markup would be:
edit: the advantage of the comment is that it can easily be used in regular source code. |
I'm wondering if you've had a chance to think a bit more on this. Of the options I've seen so far, I'm still partial to mine - it seems the simplest and least surprising/contrived, and is reasonably generalizable outside of the direct defines case as well (i.e. transitive defines, or some other inlining/defaulting that results in |
In the grand scheme of things, I think it's unrealistic that someone would So I think the correct course of action, is to create a new flag for the I think this will greatly improve how people use |
But does Or are you referring to the explicit
There's no
But without the more general treatment, the extra |
@shicks I see what you mean! In that case let me suggest changes in the PR. For starters, we shouldn't use |
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.
Some changes that need to happen, so this transformation doesn't remove any side effects which might be left behind in the object.
Also, please change the option name to be unsafe_proto
:)
@@ -6973,6 +6997,9 @@ AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { | |||
}); | |||
} | |||
} | |||
if (i < 0 && collapse_missing_props && !objectPrototypeProperties.has(key)) { | |||
return make_node(AST_Undefined, 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.
This will get rid of side effects in other properties of the object. Picture this (too contrived) use case:
const x = {
iHaveSideEffects: sideEffects(),
foo
}.bar
If we turned this object into undefined
outright here, the call with side effects disappears. There's a method which can fix this, AST_Node#drop_side_effect_free
which when called on the object X is being initialised to, would return simply sideEffects()
. Be careful because when nothing has side effects this method just returns null
.
But! We want the result of the expression to really be undefined
.
So we can call make_sequence
with an array of two which results in (sideEffects(), undefined)
. The sequence expression is terrible in terms of readability, but it's used extensively in Terser for being an expression instead of a statement.
There is, however, some calling code of flatten_object
which expects the return to be either null or a AST_Sub
with a number. I'll leave that one up to you but maybe returning a pair would work ;)
for (var i = props.length; --i >= 0;) { | ||
var prop = props[i]; | ||
if (prop.value instanceof AST_Accessor) collapse_missing_props = 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.
I don't think there's a need for this condition here. If any of the properties is an accessor, it can still be doomed to oblivion :)
Fixes #584
This is safe unless the code is relying on non-standard properties being defined on Object.prototype, which is why the optimization is protected behind the "unsafe" option.
Enabling this allows the following general pattern:
If compiled with
--define process.env={}
as the last define, then it allows individually toggling (in this case) asserts, but if nothing is specified then it will smoothly fall back on NODE_ENV. In addition to debugging features this can be useful for differential compilation (e.g. a "master" switch for "assume browsers released after 20XX" that provides reasonable defaults for avoiding feature detection in most cases, but also providing fine-grained overrides for individual features that may be more important to actually detect for one application over another.