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

Multiple ways of non-constructors throwing when constructed have different semantics #174

Closed
littledan opened this Issue Nov 13, 2015 · 2 comments

Comments

Projects
None yet
2 participants
@littledan
Member

littledan commented Nov 13, 2015

There are two ways that a function in ES6 can be "just a function, not a constructor":

  • Use the default Function [[Construct]] method, which calls the function with the appropriate new.target, and throw an exception if new.target is undefined
  • Implement only the [[Call]] internal method on a particular object, and not the [[Construct]] method. This is the strategy used for classes, and the prose at the top of the %Math% object definition also seems to imply that it is similar.

In https://code.google.com/p/v8/issues/detail?id=4538 , V8 engineers mention that there are different semantics to these two strategies: It changes whether IsConstructor is true. IsConstructor is called all over the place, but the most common callsites have implications with cross-realm access (in a way that's a bit outside of the direct ECMA spec): In places that construct, such as new or Reflect.construct, IsConstructor is called to see if an exception should be thrown.

So there are two places where an exception could be thrown, depending on the strategy: In the caller (the place where new is invoked, for example) and in the callee (the actual running constructor). This is visible in cross-realm settings because a different sort of exception will be thrown when the callee is in a different realm. In the former case, it will be the caller's realm, and in the latter case, it will be the callee's realm.

In offline discussion at the last TC39 meeting following Yehuda's call construct proposal, a few committee members discussed these two different implementations of implementing [[Call]] and [[Construct]]. The argument made was that, since Reflect.construct requires a constructor for newTarget, the distinction should be unobservable and it doesn't matter whether we go with separate [[Call]]/[[Construct]] methods or a conditional within the constructor. However, in this exception-throwing case, there appears to be a significant difference.

I bet this subtlety was entirely accidental, and that better semantics would be for the caller's realm to always throw the exception. This would give more information to users and be reasonable to implement.

@anba

This comment has been minimized.

Show comment
Hide comment
@anba

anba Nov 18, 2015

Contributor

There are two ways that a function in ES6 can be "just a function, not a constructor":

  • Use the default Function [[Construct]] method, which calls the function with the appropriate new.target, and throw an exception if new.target is undefined

I guess you mean "new.target is not undefined"?

  • Implement only the [[Call]] internal method on a particular object, and not the [[Construct]] method. This is the strategy used for classes, and the prose at the top of the %Math% object definition also seems to imply that it is similar.

I don't understand this part. %Math% is not a function object, it has neither a [[Call]] nor a [[Construct]] internal method, therefore I don't see the connection between %Math% and other callable objects. And assuming classes refers to function objects created by the class keyword, those objects do have a [[Construct]] internal method.

Contributor

anba commented Nov 18, 2015

There are two ways that a function in ES6 can be "just a function, not a constructor":

  • Use the default Function [[Construct]] method, which calls the function with the appropriate new.target, and throw an exception if new.target is undefined

I guess you mean "new.target is not undefined"?

  • Implement only the [[Call]] internal method on a particular object, and not the [[Construct]] method. This is the strategy used for classes, and the prose at the top of the %Math% object definition also seems to imply that it is similar.

I don't understand this part. %Math% is not a function object, it has neither a [[Call]] nor a [[Construct]] internal method, therefore I don't see the connection between %Math% and other callable objects. And assuming classes refers to function objects created by the class keyword, those objects do have a [[Construct]] internal method.

@littledan

This comment has been minimized.

Show comment
Hide comment
@littledan

littledan Nov 18, 2015

Member

Not sure what I was thinking here; this doesn't make sense.

Member

littledan commented Nov 18, 2015

Not sure what I was thinking here; this doesn't make sense.

@littledan littledan closed this Nov 18, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment