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

Normative: do not call super constructor when ThisBindingStatus is already initialized #762

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

bterlson
Copy link
Member

@bterlson bterlson commented Jan 5, 2017

Given:

class A { constructor() { print('a') } };
class B extends A {
  constructor() {
    super();
    super();
  }
}

The current spec says 'a' is printed twice as the second super will
invoke the super constructor before throwing an error due to this
being initialized.

This change throws just before invoking the super constructor if this
has already been initialized. The argument list is evaluated as it
may initialize this by invoking super (i.e. super(super())).

Current implementations agree on the current spec behavior so it's
questionable whether this change is worth doing. However this came up in
real world code (due to a merge mishap) and caused significant confusion
so if we can get away with changing it I think we should.

…ready initialized

Given:

```js
class A { constructor() { print('a') } };
class B extends A {
  constructor() {
    super();
    super();
  }
}
```

The current spec says 'a' is printed twice as the second super will
invoke the super constructor before throwing an error due to `this`
being initialized.

This change throws just before invoking the super constructor if this
has already been initialized. The argument list is evaluated as it
may initialize `this` by invoking super (i.e. `super(super())`).

Current implementations agree on the current spec behavior so it's
questionable whether this change is worth doing. However this came up in
real world code (due to a merge mishap) and caused significant confusion
so if we can get away with changing it I think we should.
@bterlson bterlson added needs consensus This needs committee consensus before it can be eligible to be merged. needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 labels Jan 5, 2017
@bterlson
Copy link
Member Author

bterlson commented Jan 5, 2017

I could not find any test262 tests validating that the super constructor is called twice, only that an error is thrown. Only relevant test I found is test\language\expressions\super\call-bind-this-value-twice.js.

@caiolima
Copy link
Contributor

caiolima commented Jan 5, 2017

I think it is a fair modification. As @bterlson pointed that it confuses debugging a code, I can imagine cases that aren't common, but can be written, when constructor has some side effects in the app in general. If we throw an error before calling the constructor, it makes debugging less painful.

@littledan
Copy link
Member

The semantics seem fairly reasonable to me intuitively. I was surprised when I learned that the super constructor is executed multiple times like this. Too bad there are no test262 tests, cc @leobalter .

In general, the new semantics will require implementations to do a check both before and after (as your patch does), since something that ran as part of the super constructor may have actually filled in the this value. Compilers will be able to eliminate that sometimes, but not all of the time (especially in an interpreter or baseline compiler). So this might make subclass construction (slightly) slower.

Given the potential (admittedly likely small) real slowdown, maybe this would be a normative change to get implementation feedback on before merging.

@ljharb
Copy link
Member

ljharb commented Jan 6, 2017

@littledan I'm not sure I understand - how can super() be called the first time and not have filled in the this value, such that you don't have to do any check until the next super call?

@rwaldron
Copy link
Contributor

rwaldron commented Jan 6, 2017

@littledan I wrote all of the original class tests for super()... I guess I didn't consider multiple super() calls 🤷‍♀️

@bakkot
Copy link
Contributor

bakkot commented Jan 6, 2017

@ljharb two calls can be occurring simultaneously.

@shvaikalesh
Copy link
Member

This PR reminded me of what custom elements constructors do (see step 9 of this algorithm). Definitely 👍 for handling this on ECMAScript spec level for consistency.

@ljharb
Copy link
Member

ljharb commented Jan 6, 2017

@bakkot gotcha, thanks

@bterlson
Copy link
Member Author

Discussed at TC39. No consensus for now, but if engines can implement this fix without performance degradation, it should achieve consensus. /cc @ajklein @msaboff

@ajklein
Copy link
Contributor

ajklein commented Feb 6, 2017

My initial investigation into this suggested it's not at all straightforward in V8 to eliminate the extra check. Curious for @msaboff's thoughts.

@msaboff
Copy link
Contributor

msaboff commented Feb 7, 2017

My initial look at JSC code leads me to the same conclusion as @ajklein. We need to emit the second check in our bytecode. It would take a little work to eliminate the second check.

@JemiloII
Copy link

I'm just curious, why would someone want to call super twice, or more? What benefit does it bring?

@claudepache
Copy link
Contributor

I'm just curious, why would someone want to call super twice, or more? What benefit does it bring?

Nobody would do that on purpose. This PR is about improving debugging experience.

@littledan
Copy link
Member

Thanks for looking into the implementation issues for this proposal, @ajklein and @msaboff . Now that we know that it won't be completely trivial to optimize away in all cases, how should we proceed? Would anyone be interested in making a draft implementation to understand the overhead in practice? Or, are the implementation concerns sufficient to deter us from this proposal?

@ljharb
Copy link
Member

ljharb commented Aug 22, 2018

ping @ajklein @msaboff

@ajklein
Copy link
Contributor

ajklein commented Aug 22, 2018

My opinion is that changing this isn't worth any runtime cost at all. I'd be curious for @bterlson's opinion on that question as well.

@ljharb ljharb force-pushed the master branch 3 times, most recently from 3d0c24c to 7a79833 Compare June 29, 2021 02:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs consensus This needs committee consensus before it can be eligible to be merged. needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet