Commit Graph

4 Commits

Author SHA1 Message Date
Boshen 1193c9dbaf chore(oxfmt): dogfood operator position at line start (#25937)
dogfood
2026-08-20 14:36:47 +00:00
overlookmotel 0b7ce7edd9 perf(linter/plugins): create global prop vars at top level of modules (#22928)
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.
2026-06-03 00:53:31 +00:00
overlookmotel fe24afed5e perf(linter/plugins): apply replace globals TSDown plugin to JS files (#20305)
TSDown build uses a custom plugin to replace global property lookups e.g. `Object.assign` with top level vars e.g. `const ObjectAssign = Object.assign;`, and then replace the usages with those top-level vars.

The plugin was only applied to `.ts` files. Broaden the filter to include `.js` files too, so it applies to the large generated files `deserialize.js` and `walk.js`.
2026-03-12 19:22:57 +00:00
overlookmotel c6cbe42e17 refactor(linter/plugins): split TSDown plugins into separate files (#20304)
Pure refactor. TSDown config had got very large with the custom plugins to replace `debugAssert` calls, and hoist globals vars to top level. A PR to follow will make the situation worse, as it adds another plugin.

Move the plugins into separate files, to bring the TSDown config back to a manageable size.

This PR does not alter the plugin code, just moves it.
2026-03-12 19:22:57 +00:00