Commit graph

7689 commits

Author SHA1 Message Date
Boshen
c0a3ddac28 fix(minifier): instanceof has error throwing side effect (#8378) 2025-01-09 06:49:27 +00:00
Yuji Sugiura
c1c0d71162
refactor(prettier): Make hardline! to return Doc (#8379)
For cleaner Doc construction.
2025-01-09 14:48:52 +08:00
Dunqing
0903501a98 refactor(semantic): check super usage based on scope (#8377)
No stronger opinion on both implementations, I just think the scope-based is clearer, and iterating over scope is always faster than node. The only bad point in this implementation we don't have specific ScopeFlags for class methods, thus we need to check if the parent of the function is a `MethodDefinition`
2025-01-09 06:38:09 +00:00
Dunqing
79a8fc6f7d fix(semantic): report error for super property appearing in function body (#8376)
Missing error that super property inside plain function.

```js
class C {
  constructor() {
    function g() {
      // * It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true.
      super();
    }
  }
  method() {
    function func() {
      // It is a Syntax Error if FunctionBody Contains SuperProperty is true.
      super.good();
    }
  }
}
```

I am not sure why test262 doesn't cover tests like that
2025-01-09 06:38:09 +00:00
Boshen
5516f7fcb9 fix(minifier): do not fold object comparisons (#8375) 2025-01-09 05:47:35 +00:00
Boshen
a1752a062c fix(codegen): fix incorrect minified return 1n output (#8374) 2025-01-09 05:01:41 +00:00
Boshen
cb098c7d3c fix(minifier): computed property key prototype cannot be changed (#8373) 2025-01-09 04:24:03 +00:00
Boshen
82ee77ef19 fix(minifier): do not remove shadowned undefined in return statement (#8371)
closes #8370
2025-01-09 04:03:18 +00:00
sapphi-red
814da55f81
feat(minifier): compress x = x || 1 to x ||= 1 (#8368)
The simplified version of the evaluation of `a = a || b` is:

> AssignmentExpression : LeftHandSideExpression = LogicalORExpression || LogicalANDExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let llRef be ? Evaluation of LogicalORExpression.
> 3. Let llVal be ? GetValue(llRef).
> 4. If ToBoolean(llVal) is true
>   a. Perform ? PutValue(lRef, llVal).
>   b. return llVal.
> 5. Let lrRef be ? Evaluation of LogicalANDExpression.
> 6. Let rRef be ? GetValue(lrRef).
> 7. Let rVal be ? GetValue(rRef). [Note GetValue(rRef) returns rRef itself]
> 8. Perform ? PutValue(lRef, rVal).
> 9. Return rVal.

The simplified version of the evaluation of `a ||= b` is:

> AssignmentExpression : LeftHandSideExpression ||= AssignmentExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let lVal be ? GetValue(lRef).
> 3. If ToBoolean(lVal) is true, return lVal.
> 4. Let rRef be ? Evaluation of AssignmentExpression.
> 5. Let rVal be ? GetValue(rRef).
> 6. Perform ? PutValue(lRef, rVal).
> 7. Return rVal.

The difference of these is that

- the evaluation of `a` is done twice for `a = a || b`, one with `1. Let lRef be ? Evaluation of LeftHandSideExpression` and one with `2. Let llRef be ? Evaluation of LogicalORExpression.`. This is same with #8366, #8367.
- `PutValue(lRef, llVal)` is performed when `ToBoolean(lVal)` is `true`.

So `x = x || 1` can be compressed to `x ||= 1` when the conditions written in #8366 are met and `PutValue(lRef, llVal)` does not have a side effect. When `a` is a non-global identifier (and not a reference created by a `with` statement), these conditions are met.

**References**
- [Spec of `||`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators-runtime-semantics-evaluation)
- [Spec of `=` / `||=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators-runtime-semantics-evaluation)
2025-01-09 03:47:12 +00:00
sapphi-red
a596821d85
feat(minifier): compress a.b = a.b + c to a.b += c (#8367)
The simplified version of the evaluation of `a += b` is:

> AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let lVal be ? GetValue(lRef).
> 3. Let rRef be ? Evaluation of AssignmentExpression.
> 4. Let rVal be ? GetValue(rRef).
> 5. Let r be ? ApplyStringOrNumericBinaryOperator(lVal, opText, rVal).
> 6. Perform ? PutValue(lRef, r).
> 7. Return r.

The simplified version of the evaluation of `a = a + b` is:

> AssignmentExpression : LeftHandSideExpression = AssignmentExpressionLeft + AssignmentExpressionRight
> 1. Let lRef be ? Evaluation of LeftHandSideExpression.
> 2. Let alRef be ? Evaluation of AssignmentExpressionLeft.
> 3. Let alVal be ? GetValue(alRef).
> 4. Let arRef be ? Evaluation of AssignmentExpressionRight.
> 5. Let arVal be ? GetValue(arRef).
> 6. Let rRef be ? ApplyStringOrNumericBinaryOperator(alVal, opText, arVal).
> 7. Let rVal be ? GetValue(rRef). [Note GetValue(rRef) returns rRef itself]
> 8. Perform ? PutValue(lRef, rVal).
> 9. Return rVal.

The difference of these is that the evaluation of `a` is done twice for `a = a + b`, one with `1. Let lRef be ? Evaluation of LeftHandSideExpression` and one with `2. Let alRef be ? Evaluation of AssignmentExpressionLeft.`

So this is same with #8366 and can be compressed similarly when the conditions are met (`a.b = a.b + c` -> `a.b += c`).

**References**
- [Spec of `=`, `+=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-assignment-operators-runtime-semantics-evaluation)
- [Spec of `+`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-addition-operator-plus-runtime-semantics-evaluation)
2025-01-09 03:47:11 +00:00
sapphi-red
579eb603d5
feat(minifier): compress a.b || (a.b = c) to a.b ||= c (#8366)
As noted in https://github.com/oxc-project/oxc/pull/8315, the only difference of `x ||= y` and `x || (x = y)` is that `Let lRef be ? Evaluation of LeftHandSideExpression` is done twice.

If the LeftHandSideExpression is a MemberExpression, `GetValue(baseReference)` is the only place that might have a side effect. Further more, if the `baseReference` is an IdentifierReference that is not a global reference (and not a reference created by a `with` statement), `GetValue(baseReference)` does not have a side effect.

When those conditions are met, `a.b || (a.b = c)` can be compress to `a.b ||= c`.

**References**
- [Spec of `a.b`](<https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors-runtime-semantics-evaluation:~:text=%2C%20strict).-,MemberExpression,4.%20Return%20MakePrivateReference(baseValue%2C%20fieldNameString).,-CallExpression>)
- [Spec of `GetValue`](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-getvalue)
- [Spec of `IdentifierReference`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-identifiers-runtime-semantics-evaluation)
-  [Spec of `DeclarativeEnvironmentRecord.GetBindingValue`](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-declarative-environment-records-getbindingvalue-n-s)
2025-01-09 03:47:10 +00:00
overlookmotel
aa5e65ff3c refactor(transformer/private-methods): simplify finding parent statement of class expression (#8364)
Pure refactor. Simplify code a little.
2025-01-09 03:17:41 +00:00
overlookmotel
c786fd1add refactor(transformer/private-methods): TODO comments (#8363) 2025-01-09 03:17:40 +00:00
overlookmotel
ac72adbade fix(transformer/private-methods): fix panic if instance private accessor in class (#8362)
Fix panic if a private accessor is present in class which also has property. e.g.:

```js
let C = class C {
  prop = 1;
  accessor #private = 2;
};
```

Panic occurred due to trying to unwrap `brand` property, but it's `None` because no private instance methods in the class.
2025-01-09 03:17:39 +00:00
overlookmotel
f1f129b09d fix(transformer/private-methods): create brand binding var in hoist scope (#8361)
Brand binding is a `var`, so is bound in hoist scope.
2025-01-09 03:17:38 +00:00
overlookmotel
ab6142503f fix(transformer/private-methods): no temp var for class when unused private methods (#8360)
No temp var is required for class if it contains static private method which is not referenced within class. e.g.:

```js
let C = class {
  static #method() {}
};
```

->

```js
let C = class {};
function _method() {}
```
2025-01-09 03:17:37 +00:00
camc314
f367a16364 feat(minifier): port esbuild conditional expr minification (#8351)
still some TODOs, but the main framework is there
2025-01-09 02:49:33 +00:00
Boshen
09f0f483f6 refactor(minifier): remove the buggy minimize_exit_points implementation (#8349) 2025-01-09 02:49:32 +00:00
翠 / green
3ba0b5316d
chore(tasks/coverage): fix test262 cases to run on windows (#8358)
`skip_test_path` was not working on windows because the path included
`\\`.
2025-01-09 10:48:37 +08:00
Alexander S.
b6c1546d07
test(linter): use plugin name instead of category for finding rule (#8353)
needed for #8329

I wanted to use `proc_macro::Span::SourceFile` to auto detect the plugin
name. But this feature is unstable :/
2025-01-09 10:48:11 +08:00
overlookmotel
78d7c97357 fix(transformer/typescript): create Reference for Infinity (#8354)
Create a `Reference` when generating new `IdentifierReference` for `Infinity`.
2025-01-09 02:26:33 +00:00
overlookmotel
6790d1d219 refactor(transformer/class-properties): simplify determining if class is declaration (#8357)
Small improvement. Use `Class::type` field to determine if class is declaration/expression. Not sure why it did it in such a complicated way previously!
2025-01-09 02:20:50 +00:00
Dunqing
e4d66e4636 fix(transformer/arrow-functions): store super_methods on a Stack to fix nested async methods (#8331)
In the following case, async methods can be nested in another async method. The implementation is changing to store `super_methods` on a stack, and then we can store super method information in the correct `super_methods` map.

```js
const outer = {
  value: 0,
  async method() {
    () => super.value;

    const inner = {
      value: 0,
      async method() {
        () => super.value;
      }
    };

    () => super.value;
  }
};
```
2025-01-08 21:00:45 +00:00
overlookmotel
e0a09ab023 docs(data_structures): improve docs for stack types (#8356)
Improve docs for `Stack`, `NonEmptyStack` and `SparseStack`.
2025-01-08 20:49:28 +00:00
Dunqing
fb389f724a refactor(transformer/arrow-function): create a new ident instead of clone (#8338)
There seems no reason to use a cloned identifier.
2025-01-08 19:43:53 +00:00
sapphi-red
8d52cd0a5e
feat(minifier): merge assign expression in conditional expression (#8345)
compresses `a ? b = 0 : b = 1` into `b = a ? 0 : 1`

This can be done when `b` is an IdentifierReference and the assignment operator is `=`.

In this circumstance, the evaluation of `b = a ? 0 : 1` is:

1. Let lref be ? Evaluation of LeftHandSideExpression. (this does not have a side effect when LeftHandSideExpression is an IdentifierReference)
2. Let rref be ? Evaluation of AssignmentExpression. (ConditionalExpression is evaluated here)
3. Let rval be ? GetValue(rref).
4. Perform ? PutValue(lref, rval).
5. Return rval.

**References**
- [spec of `=`](https://262.ecma-international.org/15.0/index.html#sec-assignment-operators-runtime-semantics-evaluation)
- [spec of `? :`](https://262.ecma-international.org/15.0/index.html#sec-conditional-operator-runtime-semantics-evaluation)
2025-01-08 13:34:12 +00:00
Boshen
5a648bc3cd fix(codegen): fix white space issue with do statements (#8348) 2025-01-08 13:28:10 +00:00
sapphi-red
3149fe0aa4
test(minifier): add anonymous function test case for logical expression to logical assignment compression (#8347)
Added a test case that tells having an anonymous function on the right hand side for "logical expression to logical assignment" compression is fine.
2025-01-08 13:22:50 +00:00
Boshen
9a5c66ac6a refactor(minifier): clean up (#8346) 2025-01-08 13:04:10 +00:00
sapphi-red
a69d15f299
feat(minifier): compress new Array(2) -> [,,] (#8344)
For an integer value `n` smaller than 6, `Array(n)` can be compressed to `[,,]` (the number of `,` is `n`).
2025-01-08 12:07:46 +00:00
sapphi-red
819c47536c
feat(minifier): compress new Array(7n) -> [7n] (#8343)
`Array(7n)` evaluates to `[7n]`
2025-01-08 12:07:45 +00:00
Boshen
e085d66c78 feat(minifier): remove empty IIFE (#8340) 2025-01-08 09:34:10 +00:00
Boshen
2c2e483d35 feat(minifier): fold object spread ({ ...null }) -> ({}) (#8339) 2025-01-08 09:11:28 +00:00
Yuji Sugiura
8e3eed7562
refactor(prettier): Update tasks/prettier to correctly handle snapshots (#8337)
I refactored the code in `tasks/prettier_conformance` primarily to make
the output more readable when using `--filter`.

But I also discovered that our previous implementation did not correctly
handle Prettier's behavior of adding a blank line at the EOF.

In addition, I resolved a problem where test specs that used patterns
like `runFormatTest(_, parsers)` were unable to locate the correct
snapshot output.

As a result, compatibility has also improved slightly. 😉

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-01-08 17:10:30 +08:00
Boshen
6220e05f38 feat(minifier): remove empty if statment if (test) {} -> test (#8336) 2025-01-08 08:50:10 +00:00
Boshen
a76dfae8e3 feat(minifier): remove label statement with empty body (#8333) 2025-01-08 07:38:39 +00:00
Boshen
e88a6bdd94 feat(minifier): minimize !0 + null !== 1 -> !0 + null != 1 (#8332) 2025-01-08 04:36:42 +00:00
Boshen
98f2b1ccd5 refactor(minifier): clean up peephole_substitute_alternate_syntax (#8327) 2025-01-08 03:44:19 +00:00
overlookmotel
ce6c4457c2 refactor(transformer/arrow-functions): add TODO comments (#8328)
Add some TODO comments for this transform.
2025-01-08 01:52:46 +00:00
overlookmotel
73d0025a75 refactor(transformer/arrow-functions): reduce repeated code (#8323)
Follow-on after #8024. Pure refactor. Make `self.visit_statement(stmt)` only get called in one place, rather than 2.
2025-01-08 01:52:45 +00:00
overlookmotel
62e3f7e525 perf(transformer/arrow-functions): reduce size of inlined visitor (#8322)
Follow-on after #8024.

Move code out of `visit_expression`.

`visit_expression` is marked `#[inline]`, which is a good idea because it's a hot path and most of the time the `Expression` is not `super()` and the function does nothing.

But this means we want `visit_expression` to be as small as possible to reduce the cost of inlining it. So only retain the fast path for "Is it `super()`? No it's not, so just continue traversal." and move everything else out into a separate function.
2025-01-08 01:52:45 +00:00
overlookmotel
3dd08e9786 refactor(transformer/arrow-functions): do not inline non-trivial visitor method (#8321)
Follow-on after #8024. Don't inline `visit_statements` visitor method. This visitor is not so small, and always executes unconditionally. The compiler can decide whether to inline it or not. Where `#[inline]` is really valuable is:

1. Where the function is tiny so the cost of moving it into the caller is small. and
2. The function gets called a lot e.g. `visit_expression`, `visit_statement`, `visit_identifier_reference`.
3. Most commonly the function checks something and exits "nothing to do here". e.g. `visit_expression` where you only need to transform a particular type of `Expression`.
2025-01-08 01:52:44 +00:00
overlookmotel
ea9cefb5c3 refactor(transformer/arrow-functions): reorder visitor methods (#8320)
Follow-on after #8024. Pure refactor.

Re-order the methods of the `ConstructorBodyThisAfterSuperInserter` visitor. Intent is that the call path flows from top to bottom, so the "story" makes sense when you read the code starting from the top.
2025-01-08 01:52:43 +00:00
overlookmotel
37199a4cd6 refactor(transformer/arrow-functions): rename lifetime (#8319)
Follow-on after #8024. Pure refactor. Rename lifetime to `'v` ("v" for visitor).
2025-01-08 01:52:43 +00:00
1zumii
45eb35434d
chore(linter): add vscode debugger launch config (#8271)
refer to this
[reply](https://discord.com/channels/1079625926024900739/1080101477672046712/1265758176754794598),
add CodeLLDB launch config for debugging oxlint.

usage for real dev like:
```json
{
  "type": "lldb",
  "request": "launch",
  "name": "Debug Oxlint",
  "cargo": {
    "env": {
      "RUSTFLAGS": "-g"
    },
    "args": ["build", "--bin=oxlint", "--package=oxlint"],
    "filter": {
      "name": "oxlint",
      "kind": "bin"
    }
  },
  "args": ["--ignore-pattern=./ignore/**"],
  "cwd": "/home/xxx/temp/oxlint-7102"
}
```

besides, i tired add config in `args` to override `Cargo.toml`
`[profile.dev] debug = false`, but cargo in CodeLLDB throw error.
```json
"args": [
  "build", "--bin=oxlint", "--package=oxlint", 
  "--config profile.dev.debug=true"
]
```

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-01-08 09:51:47 +08:00
overlookmotel
aebe0ea46d perf(transformer/arrow-functions): use NonEmptyStack instead of Stack (#8318)
Follow-on after #8024.

Use `NonEmptyStack` instead of `Stack`.

`NonEmptyStack` is cheaper as `last` / `last_mut` always returns `T`, rather than `Option<T>`, so there's no need to branch on whether it's `Some` or not.

The only downside of `NonEmptyStack` is that it always contains 1 dummy entry at the start (so that it's never empty). This means that it always allocates, unlike `Stack` which doesn't allocate unless you push something into it.

So `NonEmptyStack` is always the better choice unless either:

1. The stack will likely never have anything pushed to it. or
2. The type the stack holds is large or expensive to construct, so there's a high cost in having to create an initial dummy value.

In this case:

* The type is `bool` - very small and cheap to construct.
* The stack will be pushed to when entering a function. Very few JS files contain no functions at all, so the stack will always need to allocate, even if we used `Stack`.

So this case doesn't satisfy either of the circumstances in which `Stack` is the better choice.
2025-01-08 01:32:56 +00:00
overlookmotel
57e9dcf046 refactor(transformer/arrow-functions): shorten AstBuilder call (#8317)
Follow-on after #8024. Pure refactor, just shorten code.
2025-01-08 01:32:55 +00:00
sapphi-red
ec88c68c28
feat(minifier): compress a || (a = b) to a ||= b (#8315)
Compress `a || (a = b)` to `a ||= b` and for other logical operators that are possible to.

I didn't find other minifiers doing this, but this is safe for identifiers. [Quoting MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment#description):

> Logical OR assignment short-circuits, meaning that `x ||= y` is equivalent to `x || (x = y)`, except that the expression x is only evaluated once.

I actually checked the spec and the only difference was that `Let lRef be ? Evaluation of LeftHandSideExpression` was done twice. Evaluating an IdentifierReference is idempotent so it should be safe to do this compression.

References:

- [Spec of `&&=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-assignment-operators:~:text=Return%20r.-,AssignmentExpression,7.%20Return%20rVal.,-AssignmentExpression)
- [Spec of `=`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-AssignmentExpression:~:text=Runtime%20Semantics%3A%20Evaluation-,AssignmentExpression,6.%20Return%20rVal.,-AssignmentExpression)
- [Spec of `&&`](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators:~:text=Runtime%20Semantics%3A%20Evaluation-,LogicalANDExpression,5.%20Return%20%3F%C2%A0GetValue(rRef).,-LogicalORExpression)

I think this is safe for `a.b || (a.b = foo)` as well because the number of `GetValue` and `SetValue` does not change, but I didn't include that in this PR.
2025-01-07 23:19:52 +00:00
sapphi-red
e6fe84d674
feat(minifier): compress a = a + b to a += b (#8314)
Compress `a = a + b` to `a += b` and for other binary operators that are possible to.
2025-01-07 23:19:50 +00:00
sapphi-red
9ea4e31ba3
feat(minifier): remove new from new Error/new Function/new RegExp (#8313)
Remove `new` from some cases:

- `new Error(...)` -> `Error(...)`: `new` can be removed unconditionally, [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-error-constructor:~:text=Thus%20the%20function%20call%20Error(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20Error(%E2%80%A6)%20with%20the%20same%20arguments.)
- `new Function(...)` -> `Function(...)`: `new` can be removed unconditionally, [spec](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-function-constructor:~:text=Thus%20the%20function%20call%20Function(%E2%80%A6)%20is%20equivalent%20to%20the%20object%20creation%20expression%20new%20Function(%E2%80%A6)%20with%20the%20same%20arguments.)
- `new RegExp(nonRegexp)` -> `RegExp(nonRegexp)`: `new` can be removed if there's no argument or the first argument is not regex object, [mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp#return_value), [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-regexp-constructor:~:text=when%20called%20as%20a%20function%20rather%20than%20as%20a%20constructor%2C%20returns%20either%20a%20new%20RegExp%20object%2C%20or%20the%20argument%20itself%20if%20the%20only%20argument%20is%20a%20RegExp%20object.). I made this to happen if the first argument is not an object.
2025-01-07 14:48:56 +00:00