fix(minifier): keep Object introspection calls on a possible Proxy#23483
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Merging this PR will not alter performance
Comparing Footnotes
|
83c8b80 to
d30cfba
Compare
Monitor OxcCommit:
|
Merge activity
|
|
Feel free to review after merging. |
…23483) ## Summary Closes #17157. `Object.keys`, `getOwnPropertyDescriptor(s)`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `getPrototypeOf`, `hasOwn`, `isExtensible`, `isFrozen`, and `isSealed` introspect their target through internal methods a `Proxy` can trap (`[[OwnPropertyKeys]]`, `[[GetOwnProperty]]`, …), so they have observable side effects when the target is a Proxy. They were treated as unconditionally pure and dropped when unused — so `Object.getOwnPropertyDescriptor(proxy, "foo")` / `Object.keys(proxy)` were silently removed, skipping the trap. They are now treated as side-effect-free only when the target is provably not a Proxy (a determined, non-null value type — i.e. a literal), unless the bundler opts out of property-read side effects. A `null`/`undefined`/missing target is also kept (it throws a `TypeError`). - `Object.create(proto)` is pure only with an object/`null` prototype and no `properties` argument (read via `[[OwnPropertyKeys]]`/`[[Get]]`). - `Object.values`/`entries` are no longer treated as pure at all — they invoke `[[Get]]` and run getters even on a plain object. - `Object.is` stays unconditionally pure. ## Credit Adapted from #21056 by @gthb. Their `proxy_sensitive_arg_index` approach is preserved; this version adds corrections for the getter-invoking methods (`values`/`entries`, and `create` with properties) and the throw-on-`null`/`undefined` cases that the original treated as droppable. Verified by `cargo test -p oxc_minifier` / `-p oxc_ecmascript`; `minsize` unchanged. cloese #21056
ccea0ac to
31bfd9b
Compare
### 💥 BREAKING CHANGES - 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a runtime option (#23574) (overlookmotel) - e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts (#23573) (overlookmotel) ### 🚀 Features - 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571) (overlookmotel) - 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378) (sapphi-red) - 53509a8 minifier: Treeshake pure typed arrays and Set/Map array literals (#23469) (Dunqing) - 09762d9 minifier: Inline const value for read-only vars (#22593) (Dunqing) ### 🐛 Bug Fixes - 20375f9 react_compiler: Keep imports referenced only by a computed key (#23586) (Boshen) - 31bfd9b minifier: Keep Object introspection calls on a possible Proxy (#23483) (Dunqing) - 837a395 parser: Treat a line comment after ':' as leading, not trailing (#23515) (Dunqing) - e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string argument (#23470) (Dunqing) - ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457) (camc314) ### ⚡ Performance - cf24329 mangler: Compile slot sort once instead of per CAPACITY (#23577) (Boshen) - 4058a6a parser: Reduce code bloat from verify_modifiers monomorphization (#23576) (Boshen) - 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel) - dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace lowering (#23516) (Yunfei He) - 970e09a minifier: Compute template-literal inline checks in a single pass (#23467) (Yunfei He) - 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count and reuse stats in mangler builds (#23352) (Boshen) - d1fa6e0 minifier: Evaluate ternary branches once in minimize_conditional_expression (#23479) (Yunfei He) - 3fa8051 transformer: Pre-size JSX props vec to attribute count (#23466) (Yunfei He) - 488b382 react_compiler: Borrow binding names in prefilter instead of allocating (#23471) (Yunfei He) - bcb3894 minifier: Incremental scoping refresh, delete LiveUsageCollector (#23197) (Dunqing) ### 📚 Documentation - f68641e data_structures: Improve docs on safety contract (#23575) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
| test("Object.isExtensible()", true); | ||
| test("Object.isFrozen()", true); | ||
| test("Object.isSealed()", true); |
There was a problem hiding this comment.
| test("Object.isExtensible()", true); | |
| test("Object.isFrozen()", true); | |
| test("Object.isSealed()", true); | |
| test("Object.isExtensible()", false); | |
| test("Object.isFrozen()", false); | |
| test("Object.isSealed()", false); |
These does not throw an error if the argument is not an object.
https://tc39.es/ecma262/#sec-object.isextensible
…23483) ## Summary Closes #17157. `Object.keys`, `getOwnPropertyDescriptor(s)`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `getPrototypeOf`, `hasOwn`, `isExtensible`, `isFrozen`, and `isSealed` introspect their target through internal methods a `Proxy` can trap (`[[OwnPropertyKeys]]`, `[[GetOwnProperty]]`, …), so they have observable side effects when the target is a Proxy. They were treated as unconditionally pure and dropped when unused — so `Object.getOwnPropertyDescriptor(proxy, "foo")` / `Object.keys(proxy)` were silently removed, skipping the trap. They are now treated as side-effect-free only when the target is provably not a Proxy (a determined, non-null value type — i.e. a literal), unless the bundler opts out of property-read side effects. A `null`/`undefined`/missing target is also kept (it throws a `TypeError`). - `Object.create(proto)` is pure only with an object/`null` prototype and no `properties` argument (read via `[[OwnPropertyKeys]]`/`[[Get]]`). - `Object.values`/`entries` are no longer treated as pure at all — they invoke `[[Get]]` and run getters even on a plain object. - `Object.is` stays unconditionally pure. ## Credit Adapted from #21056 by @gthb. Their `proxy_sensitive_arg_index` approach is preserved; this version adds corrections for the getter-invoking methods (`values`/`entries`, and `create` with properties) and the throw-on-`null`/`undefined` cases that the original treated as droppable. Verified by `cargo test -p oxc_minifier` / `-p oxc_ecmascript`; `minsize` unchanged. cloese #21056
### 💥 BREAKING CHANGES - 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a runtime option (#23574) (overlookmotel) - e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts (#23573) (overlookmotel) ### 🚀 Features - 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571) (overlookmotel) - 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378) (sapphi-red) - 53509a8 minifier: Treeshake pure typed arrays and Set/Map array literals (#23469) (Dunqing) - 09762d9 minifier: Inline const value for read-only vars (#22593) (Dunqing) ### 🐛 Bug Fixes - 20375f9 react_compiler: Keep imports referenced only by a computed key (#23586) (Boshen) - 31bfd9b minifier: Keep Object introspection calls on a possible Proxy (#23483) (Dunqing) - 837a395 parser: Treat a line comment after ':' as leading, not trailing (#23515) (Dunqing) - e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string argument (#23470) (Dunqing) - ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457) (camc314) ### ⚡ Performance - cf24329 mangler: Compile slot sort once instead of per CAPACITY (#23577) (Boshen) - 4058a6a parser: Reduce code bloat from verify_modifiers monomorphization (#23576) (Boshen) - 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel) - dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace lowering (#23516) (Yunfei He) - 970e09a minifier: Compute template-literal inline checks in a single pass (#23467) (Yunfei He) - 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count and reuse stats in mangler builds (#23352) (Boshen) - d1fa6e0 minifier: Evaluate ternary branches once in minimize_conditional_expression (#23479) (Yunfei He) - 3fa8051 transformer: Pre-size JSX props vec to attribute count (#23466) (Yunfei He) - 488b382 react_compiler: Borrow binding names in prefilter instead of allocating (#23471) (Yunfei He) - bcb3894 minifier: Incremental scoping refresh, delete LiveUsageCollector (#23197) (Dunqing) ### 📚 Documentation - f68641e data_structures: Improve docs on safety contract (#23575) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>

Summary
Closes #17157.
Object.keys,getOwnPropertyDescriptor(s),getOwnPropertyNames,getOwnPropertySymbols,getPrototypeOf,hasOwn,isExtensible,isFrozen, andisSealedintrospect their target through internal methods aProxycan trap ([[OwnPropertyKeys]],[[GetOwnProperty]], …), so they have observable side effects when the target is a Proxy. They were treated as unconditionally pure and dropped when unused — soObject.getOwnPropertyDescriptor(proxy, "foo")/Object.keys(proxy)were silently removed, skipping the trap.They are now treated as side-effect-free only when the target is provably not a Proxy (a determined, non-null value type — i.e. a literal), unless the bundler opts out of property-read side effects. A
null/undefined/missing target is also kept (it throws aTypeError).Object.create(proto)is pure only with an object/nullprototype and nopropertiesargument (read via[[OwnPropertyKeys]]/[[Get]]).Object.values/entriesare no longer treated as pure at all — they invoke[[Get]]and run getters even on a plain object.Object.isstays unconditionally pure.Credit
Adapted from #21056 by @gthb. Their
proxy_sensitive_arg_indexapproach is preserved; this version adds corrections for the getter-invoking methods (values/entries, andcreatewith properties) and the throw-on-null/undefinedcases that the original treated as droppable.Verified by
cargo test -p oxc_minifier/-p oxc_ecmascript;minsizeunchanged.cloese #21056