mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
0b7ce7edd9
Oxlint's JS-side code is processed by a TSDown plugin that transforms global property accesses to `const`s imported from `globals.ts`. e.g.:
```js
function foo(obj) {
return Object.keys(obj);
}
```
is transformed to:
```js
import { ObjectKeys } from "./utils/globals.ts";
function foo(obj) {
return ObjectKeys(obj);
}
```
The purpose of this transform is:
1\. Better perf - avoid a property lookup/check on each call.
2\. More robust in the face of user code (plugins) altering globals e.g. `Object.keys = function myWeirdFunction() {}`.
While delving into the assembly that V8 produces while reviewing #22238, I discovered that in fact the transform was having a _negative_ effect on perf, because TSDown puts the global `const`s in a separate chunk `global.js`. Because the consts are in a different module, this actually makes them _more_ costly to access, rather than less.
In fact, it turns out that TurboFan doesn't insert a check at all in the original code - it instead has machinery to trigger de-opt if the global is reassigned. So the original perf rationale for this transform was completely misguided - it doesn't in fact produce a perf gain at all! We still want to keep it for the robustness advantage, but we don't want it to be perf _loss_, as it was before this PR.
This PR fixes the perf by instead defining consts inline in each file. The example above is now transformed to:
```js
const ObjectKeys = Object.keys;
function foo(obj) {
return ObjectKeys(obj);
}
```
This also removes the need to manually maintain the `globals.ts` file. It's now automatic.
Additionally, improve the transform in a few minor ways:
1. Support deeper nested global props e.g. `Object.prototype.toString`.
2. Skip converting e.g. `Array.prototype.slice.call(arguments)` (because `.call` requires `this`, so `ArrayPrototypeSliceCall(arguments)` is incorrect).
3. Do replace methods which take `this` (e.g. `Promise.resolve`) when they're not used in a method call.
Note: The robustness advantage of this transform may appear to be marginal, and not worth the bother of having this transform at all. Personally, I think it's worthwhile. Oxlint JS-side code has access to a slice of Rust's memory as a `Uint8Array`. Writing to that buffer could cause UB, reading/writing out of bounds, or other serious problems on Rust side, so IMO it's critical to protect against that in every way we can.