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]: subclassing seems broken in older Chrome #14056
Comments
Hey @romainmenke! 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. |
It might be caused by #12115 |
@romainmenke If you have access to older Chrome versions (otherwise I have to figure out how to install them), could you test if this code works? "use strict";
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false, value: Constructor.prototype }); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }
function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
(function (cb) {
var Foo = /*#__PURE__*/function (_HTMLElement) {
_inherits(Foo, _HTMLElement);
var _super = _createSuper(Foo);
function Foo() {
_classCallCheck(this, Foo);
return _super.apply(this, arguments);
}
_createClass(Foo, [{
key: "connectedCallback",
value: function connectedCallback() {
this._baz = 'hello';
}
}, {
key: "baz",
value: function baz() {
return this._baz;
}
}]);
return Foo;
}( /*#__PURE__*/_wrapNativeSuper(HTMLElement));
var Fooz = /*#__PURE__*/function (_Foo) {
_inherits(Fooz, _Foo);
var _super2 = _createSuper(Fooz);
function Fooz() {
_classCallCheck(this, Fooz);
return _super2.apply(this, arguments);
}
_createClass(Fooz, [{
key: "connectedCallback",
value: function connectedCallback() {
_get(_getPrototypeOf(Fooz.prototype), "connectedCallback", this).call(this);
this._baz = this._baz + ' world';
}
}]);
return Fooz;
}(Foo);
customElements.define('web-test-fooz', Fooz);
document.body.appendChild(new Fooz());
var fooz = document.getElementsByTagName('web-test-fooz')[0];
console.log(fooz.baz() === 'hello world');
})(alert); I have modified Babel's output for your test replacing Object.defineProperty(Constructor, "prototype", { writable: false }); with Object.defineProperty(Constructor, "prototype", { writable: false, value: Constructor.prototype }); in the |
My change based on your comment : |
This error started to happen to several projects we have after 7.16.5 was released. It happens in Jest tests using preset'module:metro-react-native-babel-preset'. I can try and make a reproducing code sample if needed. It always fails on the constructor of an extended class. |
@yedidyak Does it fail with any exdended class, or just with classes extending native DOM classes (like (I still have to figure out how to download Chrome 36 😅) |
@nicolo-ribaudo This is a React Native project, so it's not in chrome or with HTMLElement type classes. In at least one case it's a plain JS class that's extending another plain JS class. We're running this in Jest with node 12.22.1. |
Oh thank you. A minimal example that shows the error in node 12 using plain classes would help a lot! |
Thanks! I'll try and get a full example, but a quick extract from one part that fails: State.js (transpiled from TS)
other file:
Fails on that indicated line, from |
@yedidyak Any updates on the example? I also just tried recreate in node based on your example and could not reproduce there. @nicolo-ribaudo Maybe you can request access on a service like browserstack? Otherwise I am happy to run any tweaks to the generated code on my end and post the results. |
Hi! I eventually managed to work out that it's coming in projects of ours that override I hope that's helpful in working out where it fails for you. |
Added a minimal test for class inheritance with non-html classes : https://mrhenry.github.io/web-tests/#293ee801-4d53-4261-add3-dca6b9f8015f Can't copy paste stacktraces from browserstack but recreated manually :
Same error message :
This test only uses babel, no webpack (so no {
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"bugfixes": true,
"exclude": [
"@babel/plugin-transform-regenerator"
],
"targets": {
"browsers": [
"IE >= 6",
"Opera >= 9",
"Safari >= 4",
"Chrome >= 4",
"Edge >= 12",
"Firefox >= 2"
]
},
"useBuiltIns": false
}
]
]
} |
Thanks! I figured out how to test old chrome, I'll work on this later today. |
The problem is that in older chrome versions the prototype of functions cannot be change when making it non-writable at the same time, so function fn() {}
var proto = {};
Object.defineProperty(fn, "prototype", { value: proto, writable: false });
fn.prototype === proto; // false However, this works: function fn() {}
var proto = {};
fn.prototype = proto;
Object.defineProperty(fn, "prototype", { writable: false });
fn.prototype === proto; // true |
@romainmenke Could you test if replacing the function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true },
});
Object.defineProperty(subClass, "prototype", { writable: false });
if (superClass) _setPrototypeOf(subClass, superClass);
} |
That works! |
Yes please! I'm preparing a PR while you test it. |
This is probably caused by https://bugs.chromium.org/p/v8/issues/detail?id=3334 cc @zloirock / @ljharb You might want to polyfill it in _0.prototype = _1;
NativeObjectDefineProperty(_0, "prototype", { writable: false }); if |
Running now with these changes : https://github.com/mrhenry/web-tests/pull/263/files this will take a while |
Looks promising, haven't spotted negative delta's (no new failures) |
@nicolo-ribaudo es5-shim doesn't do any defineProperty calls that have a key of "prototype" anywhere I can find. Are you suggesting patching |
Yes, since |
@nicolo-ribaudo it's not the only such issue. This is not a proper fix of this since it will break some other cases. I'll check what can be done. |
If the fix is only applied when the key is "prototype" and the descriptor contains both writable and value, what cases will it break? |
…ing "prototype" while changing writability See babel/babel#14056 (comment)
@ljharb are you sure that the current descriptor is writable and you will be able properly check it (for example https://bugs.chromium.org/p/v8/issues/detail?id=1570)? |
@zloirock so far in chrome 15-36, at least, as well as node 0.4, |
@nicolo-ribaudo the fix is in es5-shim v4.6.3. |
@ljharb try with this polyfill, for example this: !function() {
'use strict';
var O = { prototype: 1 };
Object.defineProperty(O, 'prototype', { writable: false, configurable: true });
Object.defineProperty(O, 'prototype', { value: 42, writable: false });
}() |
Are you trying to demonstrate that the problem isn't just about the value, it's that defineProperty won't make it non-writable if there's any other property in the descriptor? |
I'm trying to demonstrate that attempting to write non-writable property in the strict mode throws an error. Like !function() {
'use strict';
var O = { prototype: 1 };
Object.defineProperty(O, 'prototype', { writable: false, configurable: true });
O.prototype = 42; // !!!
Object.defineProperty(O, 'prototype', { value: 42, writable: false });
}() |
… handle writability See babel/babel#14056 (comment)
Thanks, I see the problem. Fixed in v4.6.4. |
@JakeChampion can you tell if action is needed in the polyfill-library for this? |
@nicolo-ribaudo Thank you for getting this fixed and released! If there are features you want to test in browsers you can always add them in web-tests (or via an issue) |
Awesome, thanks! 😄 |
💻
How are you using Babel?
babel-loader (webpack)
Input code
Configuration file name
No response
Configuration
https://github.com/mrhenry/web-tests/blob/main/webpack-core-web.config.js#L166-L217
Current and expected behavior
Current :
Uncaught TypeError: Cannot call a class as a function
In
function _classCallCheck
Expected subclasses to work.
Environment
Started occurring after upgrading
@babel/core
from 7.16.0 to 7.16.5Possible solution
No response
Additional context
Test results page :
https://mrhenry.github.io/web-tests/#53d613d8-773c-43d5-800b-a78aeeab8ef7
Results for chrome <=36 dropped below
1.0
indicating that the test is now failing.The only change between tests passing and tests failing is this commit : mrhenry/web-tests@16599fd
(build outputs are included in git)
Test source code :
https://github.com/mrhenry/web-tests/blob/main/specifications/whatwg/html/4.13.4.customElements.define.super/test.pure.js
Git blame for the affected build output :
https://github.com/mrhenry/web-tests/blame/main/specifications/whatwg/html/4.13.4.customElements.define.super/test.babel.js
The text was updated successfully, but these errors were encountered: