fix(es/minifier): Avoid generating mangled property names that collide with existing properties#11839
Conversation
…e with existing properties The property mangler's encoder only skipped JavaScript reserved words when emitting new names, but not the names already in use as properties in the program. The state already aggregated those names in the `unmangleable` set (reserved DOM/JS props plus all property atoms seen in the module), but `gen_name` never consulted it. As a result, for programs containing a one-letter property like `e` (also present in `domprops.json`), the encoder could emit `e` again as the mangled name for an unrelated property in a subclass, producing observable runtime shadowing. Make `gen_name` advance the encoder until a candidate that is not in `unmangleable` is produced. In practice this costs 0-2 extra encode calls per generated name. Closes swc-project#11027
🦋 Changeset detectedLatest commit: 5efb4e2 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will not alter performance
Comparing Footnotes
|
Binary Sizes
Commit: e290c52 |
|
Regarding the CodSpeed regression on |
kdy1
left a comment
There was a problem hiding this comment.
Can you add some complex test cases?
Cover three additional scenarios that exercise the property mangler's collision avoidance: - Multi-level inheritance chain (`A -> B -> C`) with multiple fields per level, ensuring no mangled name in a deeper subclass collides with a property inherited from any ancestor. - Object literal sharing the namespace with a class, including a reserved DOM/JS property (`valueOf`), confirming the mangler treats both kinds of property declarations consistently. - Mixed shapes (getter on the base, plain methods on both base and derived, instance fields on the derived), confirming the fix holds regardless of how the inherited member is declared. Requested by @kdy1 on swc-project#11839.
|
Added three additional test cases covering deeper inheritance chains, object-literal-and-class namespace sharing, and mixed shapes (getters / methods / fields). Marking as ready for review. |
|
Sorry, I should have been more specific. Please add test cases for lots of properties to mangle |
Per @kdy1's request on swc-project#11839, add a test case with a large number of properties to be mangled. The base class declares six fields whose names (`a` through `f`) are all present in `domprops.json`, and the derived class declares 25 fresh fields. Since roughly half of the single-character base54 alphabet collides with reserved DOM property names, generating 25 mangled names exercises the collision-avoidance loop in `gen_name` repeatedly, confirming the fix scales correctly when many candidates must be skipped.
|
Added |
|
Thanks for refactoring the stress test to compare runtime stdout via |
|
Thanks! |
What's the problem this PR addresses?
Closes #11027.
When
jsc.minify.mangle.propsis enabled, the property mangler can rename a property in a subclass to a name that already exists as a property of a base class (or of any other object in the program), producing observable shadowing at runtime.Minimal repro from the issue:
Before the fix,
someFieldwas mangled toe, sonew Class2().ereturned2instead of the inherited1.How did you fix it?
Root cause is in
crates/swc_ecma_minifier/src/pass/mangle_props.rs. The state already builds anunmangleableset containing both reserved DOM/JS property names (loaded fromdomprops.json/jsprops.json) and all property names seen in the program. Butgen_namecalledBase54Chars::encodedirectly, which only skips JavaScript reserved words — it never consultsunmangleable.In the repro,
eis present indomprops.json, soClass1.eis correctly preserved byis_reserved. The encoder then walksa, b, c, ...and may emiteforsomeField, colliding with the inherited property.The fix advances the encoder in a loop until it produces a candidate not in
unmangleable. The set already aggregates exactly what we need to avoid, so no extra bookkeeping is needed. In practice this is 0-2 extraencodecalls per generated name.Testing
issue_11027_inherited_class_property_collisionincrates/swc_ecma_minifier/tests/mangle.rsusing the existingassert_mangledharness withpropsmangling enabled (the fixture-based suite attests/mangle/**does not enable property mangling, so an inline test is the right vehicle here, in line with the otherprops-related tests in this file).cargo test -p swc_ecma_minifier --test manglepasses (2217 tests)../scripts/exec.shpasses (2129/2130, 1 ignored, unchanged).cargo fmt --allandcargo clippy -p swc_ecma_minifier --all-targets -- -D warningsclean.