Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Safari class fields implementation is buggy - ReferenceError: Can't find variable #14289

Closed
1 task done
liamcmitchell-sc opened this issue Feb 21, 2022 · 10 comments · Fixed by #16569
Closed
1 task done
Labels
i: browser bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@liamcmitchell-sc
Copy link

liamcmitchell-sc commented Feb 21, 2022

💻

  • Would you like to work on a fix?

How are you using Babel?

babel-loader (webpack)

Input code

This bug can be triggered by input code like the following, when compiled with webpack and a babel config with preset-env that targets latest Safari/iOS (class fields are not transformed).

import helper from './helper'

class C {
  property = helper(() => {})
}

Current and expected behavior

Extra parens in class fields cause ReferenceErrors in Safari, see webkit bug: https://bugs.webkit.org/show_bug.cgi?id=236843

Generated code will produce an error like "ReferenceError: Can't find variable a".

Until the webkit implementation is fixed, class fields should continue to be transpiled when targeting Safari/iOS.

Environment

@babel/core 7.17
@babel/preset-env 7.16.11
@babel/compat-data 7.16.8
webpack 5.38.1

Possible solution

Remove Safari and iOS from proposal-class-properties compat data:
https://github.com/babel/babel/blob/v7.17.5/packages/babel-compat-data/data/plugins.json#L19-L29

Does this require updating the upstream compatibility data?
https://github.com/kangax/compat-table

Workaround

If anyone else experiences this issue, the easy fix is to force transformation of class properties in preset-env config:

presets: [
  [
    '@babel/preset-env',
    {
      include: ['@babel/plugin-proposal-class-properties'],
    },
  ]
],
@babel-bot
Copy link
Collaborator

Hey @liamcmitchell-sc! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite.

@ashharrison90
Copy link

also hit this - just wanted to say great repro steps and workaround @liamcmitchell-sc 🙌

@JLHwung
Copy link
Contributor

JLHwung commented Feb 21, 2022

We will need a bugfix plugin so when bugfix: true is enabled (Babel 8 defaults), we only transpile class properties when

  1. User code is affected by the specific browser bug
  2. User targets to affected browser versions
    An example of bugfix plugin can be found in Implement @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression #13842, where we workaround another JSC bug.

We will need a new test in compat-table, similar to compat-table/compat-table#1765 and implement such plugin.

Experiments on this bug

Another affected example

{
  let a = [];
  new class {
    c = a[(0)]; 
  }
}

This one runs without ReferenceError, but c is correctly initialized as 42, which should have been undefined:

let a = [42];
{
  let a = [];
  new class {
    c = a[(0)]; 
  }
}

and this works as expected

let a = [];
new class {
  c = a[(0)];
}

So the bug is that, when resolving referenced identifiers within field initializer (a under c = ...), Safari miscalculate the scope of such bindings to the upper level (if exists), if such referenced identifiers are seen outside of a parenthesized expression (i).

So we don't have to apply the bug fix when

  1. the class is defined in top level (popular cases),
  2. the field initializer does not contain parenthesized expressions (popular cases)

When bugfix: false is used, we will apply the complete class-properties transform on affected Safari versions.

@liamcmitchell-sc
Copy link
Author

@JLHwung

So we don't have to apply the bug fix when

  1. the class is defined in top level (popular cases),

Do plugins know if compiled code is run at the top level or bundled into functions?

  1. the field initializer does not contain parenthesized expressions (popular cases)

The parenthesized expressions in my case are produced by Webpack Terser after all Babel transformations. Without knowing what transforms will happen after Babel, the fix would have to trigger if any variable is referenced.

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Feb 21, 2022

Do plugins know if compiled code is run at the top level or bundled into functions?

Good point, we should probably be more conservative and apply the fix even if the plugin is at the top level.

The parenthesized expressions in my case are produced by Webpack Terser after all Babel transformations

I'd consider this something to be fixed in terser, since it takes working code and produces non-working code. It's identical to how Terser has a safari10 option to avoid producing code that is buggy in Safari 10.

@nicolo-ribaudo
Copy link
Member

I propose to fix this bug by wrapping class fields initializers in an IIFE if they contain any reference to a variable defined in the parent scope, and that reference is not nested within another function:

let a = [];
{
  let b = [];
  new class {
    x = a[0];
    y = b[0];
    z = () => b[0];
  };
}

becomes

let a = [];
{
  let b = [];
  new class {
    x = a[0];
    y = (() => b[0])();
    z = () => b[0];
  };
}

I don't think we should explicitly check for parentheses, because they are hard to predict in a transform. In some cases they are explicit, while in other cases they are implicitly printed by the generator (for example, when printing a multiplication between a number and a sum). Also, even if the class field currently does not contain parentheses they could be easily introduced by new transforms that transform an expression nested within the class field value.

Also, we'll need tests to make sure that the bugfix plugin works with all our other class-related plugins.

@OliverJAsh
Copy link

Quick note to say that the WebKit bug has been fixed and no longer reproduces in Safari 16, but of course the issue still applies in Safari 15.

@7rulnik
Copy link

7rulnik commented Jan 16, 2023

Just faced and fought this bug for 3 days. So it's not clear to me where should be fixed. In babel or in terser?

@sirreal
Copy link

sirreal commented Nov 7, 2023

One important note for the workaround, if you start to use private methods you'll also need to include @babel/plugin-transform-private-methods. There seems to be a dependency where @babel/plugin-proposal-class-properties requires @babel/plugin-transform-private-methods. (This makes intuitive sense, we probably need to transform private methods if we're going to transform class properties.)

If you start to use private methods in an environment that doesn't transform them but have applied the workaround to include @babel/plugin-proposal-class-properties, you'll get an error like:

Class private methods are not enabled. Please add `@babel/plugin-transform-private-methods` to your configuration.

I would update the workaround to include both:

{
presets: [
  [
    '@babel/preset-env',
    {
      include: [
        // See https://github.com/babel/babel/issues/14289
        '@babel/plugin-proposal-class-properties',
        // Private methods cannot be used with the class properties transform, ensure they're transformed as well
        '@babel/plugin-transform-private-methods'
      ],
    },
  ]
],
}

This is easy to see in the REPL. The following fails without the additional transform, but is compiled correctly with both transforms:

class C {
  #privateProp;
  #method() {}
}

@maheshramaiah
Copy link

maheshramaiah commented Jan 4, 2024

Faced similar issue for below code snippet in Safari < 16 with error - "Reference error: Cant find variable t".

var t, a;   

class l {
    static staticVar = null === (t = window.someVariable) || void 0 === t || null === (a = t.someRandomFn("abcd")) || void 0 === a ? void 0 : a.then((e=>e));
}

The variable t was defined externally and initialized within a static variable of the class. While initializing t, the static variable failed to resolve the lexical scope for t. This issue occurred intermittently, resulting in a "Reference error: Can't find variable t." It was resolved by adding the @babel/plugin-transform-class-properties plugin.

Seeking insights into the transient nature of this error. Any ideas on what might have caused this intermittent failure?

queengooborg added a commit to mdn/browser-compat-data that referenced this issue Jun 5, 2024
* Update public_class_fields for Safari

Marks Safari 14-15 as having partial support for public class fields. While they do have a basic implementation, there is a critical bug which leads to exceptions in many cases. The exceptions are especially hard to identify/debug because they are resolved when dev-tools are open.

The bug is resolved in Safari 16.

References:

- Safari bug: https://bugs.webkit.org/show_bug.cgi?id=236843

- `babel/babel` discussion regarding adding 'bugfix' behavior for this issue: babel/babel#14289

* Update javascript/classes.json

Co-authored-by: Queen Vinyl Da.i'gyu-Kazotetsu <[email protected]>

---------

Co-authored-by: Queen Vinyl Da.i'gyu-Kazotetsu <[email protected]>
davidtaylorhq added a commit to davidtaylorhq/compat-table that referenced this issue Jun 10, 2024
davidtaylorhq added a commit to davidtaylorhq/compat-table that referenced this issue Jun 10, 2024
davidtaylorhq added a commit to davidtaylorhq/compat-table that referenced this issue Jun 10, 2024
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jun 10, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

Pending:
- acceptance of compat-table/compat-table#1892
- reviewing test failures
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jul 10, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

Pending:
- acceptance of compat-table/compat-table#1892
- reviewing test failures
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jul 11, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jul 11, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jul 11, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
davidtaylorhq added a commit to davidtaylorhq/babel that referenced this issue Jul 12, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
nicolo-ribaudo pushed a commit to davidtaylorhq/babel that referenced this issue Jul 25, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
nicolo-ribaudo pushed a commit to davidtaylorhq/babel that referenced this issue Jul 26, 2024
This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve babel#14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Oct 26, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
i: browser bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants