Repository: swc-project/swc (rust)

Issue:
minification bug: string literal .length is incorrect with non-ascii characters
### Describe the bug

It appears that swc's syntax compression for string literal length is counting codepoints instead of code units?

[`String.prototype.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length#description) uses code units to count the length of the string

The following input
```js
console.log(
   "😋📋👌".length
);
```


[In SWC, transpiles to:](https://play.swc.rs/?version=1.3.50&code=H4sIAAAAAAAAA0vOzyvOz0nVy8lP1%2BBSUFBQ%2BjB%2FRveH%2BZOBeGKPkl5Oal56SQaXpjUAp2gjDCgAAAA%3D&config=H4sIAAAAAAAAA52VS47bMAxA93OKwOsuplkMih6gu55BUCzK0VQWDZHyJBjk7qUVO5MmtBfdBDEfP%2BJP%2BnzZ7Zp3apufu0%2F5Kx%2BDzQT59i0SOie2J5E0fB6A2hwGbr4tlGlCnAtUyeUKGra5A56MgPav%2B%2F1s0EREAhF7GwlmWR9S8Of7kC32QwaiO5lIxWXpITH9az%2BzjB80n%2BROfkCMYNMGMZZMSAwdZM1xizHagcCMNiteppPaHAi1EBMsDM4MGQeVJxc4YJKYz9SBdaZFBwoKGVoOI2hmEkvMEkl6Sj4VOziUrqttfrCG0cZiWYkJp9oSOa3i9YiB2PiStBJe4UoNrnAu7qNl8CYDl5ye7d4xpJWe%2FAGQCkRLlGwPmt%2Bq4WWe1qz9pmVIXkaWzwqX%2BdayTNBJUU0IXqnsVBnIHLRuZnClhamyrXacGa%2BUj4IDA97LrCiu6SNwe9SCTnuOXgHSX%2Bu1qboCc9vCFT4txAb%2BJVmyPmCzRm%2F5uE7p3B8wbgTogY%2FoNhSkFYzrOMstcRrWeUkOZDTAqSqFKni%2BBGQBGE2s9%2BXTbMh6iEfTRTzYr9xmjcvtHu5t6urC312YjEOEEeL%2Fr8gmlbON01Qvm%2F20vfBDG0orixe%2Bvz5m8rL81pyaHl25y6fO5PU5eWu%2BlJaXY0mvCfR7Maxv0uUvkzcAxeIGAAA%3D)
```js
console.log(3);
```

Evaluating `console.log("😋📋👌".length)` in Node and Bun logs:
```js
6
```



### Input code

```typescript
if (
     // incorrect:
     "😋📋👌".length === 3

     // correct:
     // "😋📋👌".length === 6
  ) {
    // side effect
    new Response(123);
  }
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "target": "es2022",
    "loose": false,
    "minify": {
      "compress": {
        "arguments": false,
        "arrows": true,
        "booleans": true,
        "booleans_as_integers": false,
        "collapse_vars": true,
        "comparisons": true,
        "computed_props": true,
        "conditionals": true,
        "dead_code": true,
        "directives": true,
        "drop_console": false,
        "drop_debugger": true,
        "evaluate": true,
        "expression": false,
        "hoist_funs": false,
        "hoist_props": true,
        "hoist_vars": false,
        "if_return": true,
        "join_vars": true,
        "keep_classnames": false,
        "keep_fargs": true,
        "keep_fnames": false,
        "keep_infinity": false,
        "loops": true,
        "negate_iife": true,
        "properties": true,
        "reduce_funcs": false,
        "reduce_vars": false,
        "side_effects": true,
        "switches": true,
        "typeofs": true,
        "unsafe": false,
        "unsafe_arrows": false,
        "unsafe_comps": false,
        "unsafe_Function": false,
        "unsafe_math": false,
        "unsafe_symbols": false,
        "unsafe_methods": false,
        "unsafe_proto": false,
        "unsafe_regexp": false,
        "unsafe_undefined": false,
        "unused": true,
        "const_to_let": true,
        "pristine_globals": false
      },
      "mangle": {
        "toplevel": true,
        "keep_classnames": false,
        "keep_fnames": false,
        "keep_private_props": false,
        "ie8": false,
        "safari10": false
      }
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": true,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.3.50&code=H4sIAAAAAAAAA1PITFPQ4FIAAX19hcy85PyiotTkEiuIkNKH%2BTO6P8yfDMQTe5T0clLz0ksyFGxtbRWMueCaULUABXDrMgOq0VSo5oIqLM5MSVVITUsD6gYL5aWWKwSlFhfk5xWnahgaGWtaA4Vrubi4AJW1%2FWumAAAA&config=H4sIAAAAAAAAA52VS47bMAxA93OKwOsuplkMih6gu55BUCzK0VQWDZHyJBjk7qUVO5MmtBfdBDEfP%2BJP%2BnzZ7Zp3apufu0%2F5Kx%2BDzQT59i0SOie2J5E0fB6A2hwGbr4tlGlCnAtUyeUKGra5A56MgPav%2B%2F1s0EREAhF7GwlmWR9S8Of7kC32QwaiO5lIxWXpITH9az%2BzjB80n%2BROfkCMYNMGMZZMSAwdZM1xizHagcCMNiteppPaHAi1EBMsDM4MGQeVJxc4YJKYz9SBdaZFBwoKGVoOI2hmEkvMEkl6Sj4VOziUrqttfrCG0cZiWYkJp9oSOa3i9YiB2PiStBJe4UoNrnAu7qNl8CYDl5ye7d4xpJWe%2FAGQCkRLlGwPmt%2Bq4WWe1qz9pmVIXkaWzwqX%2BdayTNBJUU0IXqnsVBnIHLRuZnClhamyrXacGa%2BUj4IDA97LrCiu6SNwe9SCTnuOXgHSX%2Bu1qboCc9vCFT4txAb%2BJVmyPmCzRm%2F5uE7p3B8wbgTogY%2FoNhSkFYzrOMstcRrWeUkOZDTAqSqFKni%2BBGQBGE2s9%2BXTbMh6iEfTRTzYr9xmjcvtHu5t6urC312YjEOEEeL%2Fr8gmlbON01Qvm%2F20vfBDG0orixe%2Bvz5m8rL81pyaHl25y6fO5PU5eWu%2BlJaXY0mvCfR7Maxv0uUvkzcAxeIGAAA%3D

### Expected behavior

It should evaluate to the same as what JavaScript evaluates to

### Actual behavior

_No response_

### Version

1.3.50

### Additional context

_No response_

Pull request:
fix(es/minifier): Use UTF16 length for `str.length`
**Description:**


**Related issue:**

 - Closes #7274.

Patch:
diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs
index 509bfadb4f..701a194843 100644
--- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs
+++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs
@@ -154,7 +154,7 @@ impl SimplifyExpr {
                     self.changed = true;
 
                     *expr = Expr::Lit(Lit::Num(Number {
-                        value: value.chars().count() as f64,
+                        value: value.chars().map(|c| c.len_utf16()).sum::<usize>() as _,
                         span: *span,
                         raw: None,
                     }));


Test patch:
diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs
index 20b8fe3a8e..6acfb06d7a 100644
--- a/crates/swc_ecma_minifier/tests/exec.rs
+++ b/crates/swc_ecma_minifier/tests/exec.rs
@@ -10931,3 +10931,22 @@ fn issue_6914_3() {
         false,
     );
 }
+
+#[test]
+fn issue_7274() {
+    run_default_exec_test(
+        r###"
+        if (
+            // incorrect:
+            "😋📋👌".length === 3
+    
+            // correct:
+            // "😋📋👌".length === 6
+        ) {
+            // side effect
+            new Response(123);
+        }
+        console.log('PASS');
+        "###,
+    );
+}