Skip to content

fix(es/minifier): Avoid generating mangled property names that collide with existing properties#11839

Merged
kdy1 merged 6 commits into
swc-project:mainfrom
baltasarblanco:fix/11027-mangle-props-name-collision
May 5, 2026
Merged

fix(es/minifier): Avoid generating mangled property names that collide with existing properties#11839
kdy1 merged 6 commits into
swc-project:mainfrom
baltasarblanco:fix/11027-mangle-props-name-collision

Conversation

@baltasarblanco

Copy link
Copy Markdown
Contributor

What's the problem this PR addresses?

Closes #11027.

When jsc.minify.mangle.props is 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:

class Class1 { e = 1; }
class Class2 extends Class1 { someField = 2; }
console.log(new Class2().e); // expected: 1

Before the fix, someField was mangled to e, so new Class2().e returned 2 instead of the inherited 1.

How did you fix it?

Root cause is in crates/swc_ecma_minifier/src/pass/mangle_props.rs. The state already builds an unmangleable set containing both reserved DOM/JS property names (loaded from domprops.json / jsprops.json) and all property names seen in the program. But gen_name called Base54Chars::encode directly, which only skips JavaScript reserved words — it never consults unmangleable.

In the repro, e is present in domprops.json, so Class1.e is correctly preserved by is_reserved. The encoder then walks a, b, c, ... and may emit e for someField, 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 extra encode calls per generated name.

Testing

  • Added issue_11027_inherited_class_property_collision in crates/swc_ecma_minifier/tests/mangle.rs using the existing assert_mangled harness with props mangling enabled (the fixture-based suite at tests/mangle/** does not enable property mangling, so an inline test is the right vehicle here, in line with the other props-related tests in this file).
  • cargo test -p swc_ecma_minifier --test mangle passes (2217 tests).
  • ./scripts/exec.sh passes (2129/2130, 1 ignored, unchanged).
  • cargo fmt --all and cargo clippy -p swc_ecma_minifier --all-targets -- -D warnings clean.

…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
@baltasarblanco baltasarblanco requested review from a team as code owners May 2, 2026 13:06
@changeset-bot

changeset-bot Bot commented May 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest 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

@CLAassistant

CLAassistant commented May 2, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed-hq

codspeed-hq Bot commented May 2, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 219 untouched benchmarks
⏩ 31 skipped benchmarks1


Comparing baltasarblanco:fix/11027-mangle-props-name-collision (5efb4e2) with main (9300ede)

Open in CodSpeed

Footnotes

  1. 31 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Binary Sizes

File Size
swc.linux-x64-gnu.node 27M (27759176 bytes)

Commit: e290c52

@baltasarblanco

Copy link
Copy Markdown
Contributor Author

Regarding the CodSpeed regression on es/large/codegen/es2016 (-2.23%, 37.1ms → 37.9ms): this benchmark exercises the codegen path, while this PR only modifies swc_ecma_minifier::pass::mangle_props — the change is gated behind mangle.props and never executes in a pure codegen run. CodSpeed also flagged "Different runtime environments detected" and notes that no successful main run was available, so the comparison fell back to 16a56d0 which may contain unrelated changes. The variance is consistent with runner noise (~0.8ms on a 37ms bench). Acknowledging on CodSpeed.

@kdy1 kdy1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some complex test cases?

@kdy1 kdy1 marked this pull request as draft May 4, 2026 02:05
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.
@baltasarblanco baltasarblanco marked this pull request as ready for review May 4, 2026 17:10
@baltasarblanco

Copy link
Copy Markdown
Contributor Author

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.

@kdy1

kdy1 commented May 4, 2026

Copy link
Copy Markdown
Member

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.
@baltasarblanco

Copy link
Copy Markdown
Contributor Author

Added issue_11027_many_properties_stress_test: a base class with six fields whose names (a through f) are all in domprops.json, plus a derived class declaring 25 fresh fields. The mangled output confirms none of the 25 generated names collide with the reserved-or-inherited names — the encoder skipped the unavailable single-character candidates and continued onward (i, l, o, t, m, s, g, ...), exercising the collision-avoidance loop repeatedly within a single mangle pass.

@baltasarblanco

Copy link
Copy Markdown
Contributor Author

Thanks for refactoring the stress test to compare runtime stdout via run_mangle_props_exec_test — that's a much better way to validate semantic preservation than matching the mangled output textually. I'll keep this pattern in mind for future contributions.

@kdy1

kdy1 commented May 5, 2026

Copy link
Copy Markdown
Member

Thanks!

@kdy1 kdy1 merged commit 9b4fab5 into swc-project:main May 5, 2026
68 checks passed
@github-actions github-actions Bot added this to the Planned milestone May 5, 2026
@swc-project swc-project locked as resolved and limited conversation to collaborators Jun 4, 2026
@kdy1 kdy1 modified the milestones: Planned, v1.15.41, v1.15.40 Jun 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

es/minifier: Properties of inherited class might be incorrectly renamed when props manging is enabled

3 participants