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

Derived constructors should not be allowed to return primitives #13571

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -615,9 +615,14 @@ helpers.possibleConstructorReturn = helper("7.0.0-beta.0")`
import assertThisInitialized from "assertThisInitialized";

export default function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
if (call) {
if (typeof call === "object" || typeof call === "function") {
return call;
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic doesn't work if the constructor returns, for example, 0 or null (can you add a test for a falsy value?).

It should be something like

if (call && (typeof call === "object" || typeof call === "function")) {
  return call;
} else if (call !== void 0) {
  throw new TypeError("...");
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or

if (call !== void 0) {
  if (typeof call === "object" || typeof call === "function") {
    return call;
  } else {
    // throw errors
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My version is 3 bytes shorter when using terser 😛

function f(o){if(o&&("object"==typeof o||"function"==typeof o))return o;if(void 0!==o)throw new TypeError("...")}
function g(o){if(void 0!==o){if("object"==typeof o||"function"==typeof o)return o;throw new TypeError("...")}}

Also, I think yours incorrectly allows returning null.

This comment was marked as off-topic.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but use of 'typeof' is a bad idea and kill perf.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you suggest to use instead?

Copy link

@KFlash KFlash Jul 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. 'typeof obj === 'object';' is almost ~50% slower than other 'typeof'. And 'typeof number' return NaN even if it's not an number - in some cases.

There exist 2 popular utility libraries that has an optimized check for this.

Personally I would have used some kind of meta data and bitwise masks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There exist 2 popular utility libraries that has an optimized check for this.

I only found is-object (which still uses typeof === "object") and lodash.isObject (which also uses typeof and checks if it's "object"). Why would typeof === "object" be slower than the other types?

Personally I would have used some kind of meta data and bitwise masks

o can be any possible JS value present in the JS code of our users, we cannot use bitwise mask

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot the history behind this, but AngularJS had some good checks in their earlier versions. 1x of their library. I think they checked against the constructor. Also Google had a library a few years back - forgot the name - with fast checks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 'else' is not needed. You already return in the line above.

throw new TypeError("Invalid attempt to return primitive value");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on matching v8's error?

Suggested change
throw new TypeError("Invalid attempt to return primitive value");
throw new TypeError("Derived constructors may only return object or undefined");

}
}

return assertThisInitialized(self);
}
`;
Expand Down
@@ -0,0 +1,10 @@
class Bar {}

class Foo extends Bar {
constructor() {
super();
return 3;
}
}

expect(() => new Foo()).toThrow("Invalid attempt to return primitive value");
@@ -0,0 +1,6 @@
class Foo extends Bar {
constructor() {
super();
return 3;
}
}
@@ -0,0 +1,17 @@
var Foo = /*#__PURE__*/function (_Bar) {
"use strict";

babelHelpers.inherits(Foo, _Bar);

var _super = babelHelpers.createSuper(Foo);

function Foo() {
var _this;

babelHelpers.classCallCheck(this, Foo);
_this = _super.call(this);
return babelHelpers.possibleConstructorReturn(_this, 3);
}

return Foo;
}(Bar);