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

Added runtime TypeError for non-function, non-null __extends #37283

Merged

Conversation

JoshuaKGoldberg
Copy link
Contributor

As per the linked issue, although TypeScript already has checking in place that tries to prevent users from extending classes before their declaration, it's still possible to accidentally work around the checker. This adds a block to __extends that throws a TypeError if the base class b isn't a function.

This'll be what the new error message is:

C:\Code\tstest\index.js:10
            throw new TypeError("Class extends value " + b + " is not a constructor or null");
            ^

TypeError: Class extends value undefined is not a constructor or null
    at __extends (C:\Code\tstest\index.js:10:19)
    at C:\Code\tstest\index.js:18:5
    at Object.<anonymous> (C:\Code\tstest\index.js:23:2)

My hope is that this will not have a negative performance impact on community performance-critical applications, as they would likely already prefer newer browser/Node versions and output ES2015+ code. If there is something you can think of that I should do to verify that hope, I'd love to know!

These errors match the behavior in Chrome, Node, and Firefox Runtime errors in Chrome 80 and Node 12.0.0:
class X extends null { }
// undefined

class Y extends undefined { }
// TypeError: Class extends value undefined is not a constructor or null

class Z extends 0 { }
// TypeError: Class extends value 0 is not a constructor or null

Class extends value {0} is not a constructor or null matches the Node.js behavior:

Runtime errors in Firefox 72.0.1:

class X extends null { }
// undefined

class Y extends undefined { }
// TypeError: class heritage undefined is not an object or null

class Z extends 0 { }
// TypeError: class heritage 0 is not an object or null

Fixes #5794.

As per the linked issue, although TypeScript already has checking in place that tries to prevent users from extending classes before their declaration, it's still possible to accidentally work around the checker. This adds a block to `__extends` that throws a `TypeError` if the base class `b` isn't a function.

My _hope_ is that this will not have a negative performance impact on community performance-critical applications, as they would likely already prefer newer browser/Node versions and output ES2015+ code. If there is something you can think of that I should do to verify that hope, I'd love to know!

For reference, runtime errors in Node 12.0.0 (Chrome exhibits the same messages):

```js
class X extends null { }
// undefined

class Y extends undefined { }
// TypeError: Class extends value undefined is not a constructor or null

class Z extends 0 { }
// TypeError: Class extends value 0 is not a constructor or null
```

`Class extends value {0} is not a constructor or null` matches the Node.js behavior:
* [Message template](https://github.com/nodejs/node/blob/2bdeb88c27b4d8de3a8f6b7a438cf0bcb88fa927/deps/v8/src/common/message-template.h) for `ExtendsValueNotConstructor`
* [Error thrown with that message](https://github.com/nodejs/node/blob/6ca81ad72a3c6fdf16c683335be748f22aaa9a0d/deps/v8/src/runtime/runtime-classes.cc#L617) when `!super_class->IsConstructor()`

Runtime errors in Firefox 72.0.1:

```js
class X extends null { }
// undefined

class Y extends undefined { }
// TypeError: class heritage undefined is not an object or null

class Z extends 0 { }
// TypeError: class heritage 0 is not an object or null
```
@sandersn sandersn added the For Backlog Bug PRs that fix a backlog bug label Mar 17, 2020
Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

Couple of comments, since I don't feel qualified to sign off on an emit change, especially for __extends.

  1. Whitespace changed a suspicious amount.
  2. You probably need to merge from master and accept baselines again because CI failed last time with a missed baseline.

@JoshuaKGoldberg
Copy link
Contributor Author

@typescript-bot perf test
@typescript-bot pack this

@JoshuaKGoldberg
Copy link
Contributor Author

Well, I guess @typescript-bot only listens to Microsoft employees 😄

@sandersn
Copy link
Member

Yep, sorry! Typescript-bot is (1) powerful (2) ruthless, so we don't trust anybody outside the team to command it.

@typescript-bot perf test this
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 18, 2020

Heya @sandersn, I've started to run the perf test suite on this PR at 9adf2f8. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 18, 2020

Heya @sandersn, I've started to run the tarball bundle task on this PR at 9adf2f8. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 18, 2020

Hey @sandersn, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/68578/artifacts?artifactName=tgz&fileId=33BBA8E78CE6ACA543A7BDDD60E18E89EFFF81D4A259A717EE1BF06EF6C3DB4A02&fileName=/typescript-3.9.0-insiders.20200318.tgz"
    }
}

and then running npm install.


There is also a playground for this build.

@typescript-bot
Copy link
Collaborator

@sandersn
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..37283

Metric master 37283 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 327,751k (± 0.04%) 327,210k (± 0.04%) -541k (- 0.17%) 326,746k 327,369k
Parse Time 1.63s (± 0.55%) 1.62s (± 0.74%) -0.00s (- 0.06%) 1.60s 1.65s
Bind Time 0.89s (± 0.65%) 0.88s (± 1.16%) -0.01s (- 0.68%) 0.87s 0.91s
Check Time 4.78s (± 0.69%) 4.76s (± 0.58%) -0.02s (- 0.50%) 4.70s 4.80s
Emit Time 5.29s (± 0.59%) 5.32s (± 0.93%) +0.04s (+ 0.68%) 5.25s 5.46s
Total Time 12.58s (± 0.42%) 12.59s (± 0.53%) +0.01s (+ 0.05%) 12.48s 12.79s
Monaco - node (v10.16.3, x64)
Memory used 327,018k (± 0.01%) 327,094k (± 0.02%) +75k (+ 0.02%) 326,976k 327,227k
Parse Time 1.26s (± 0.61%) 1.25s (± 0.47%) -0.01s (- 0.56%) 1.24s 1.27s
Bind Time 0.78s (± 0.44%) 0.77s (± 0.62%) -0.00s (- 0.51%) 0.76s 0.78s
Check Time 4.76s (± 0.63%) 4.75s (± 0.32%) -0.01s (- 0.21%) 4.73s 4.80s
Emit Time 2.92s (± 0.89%) 2.92s (± 0.98%) -0.01s (- 0.21%) 2.86s 3.00s
Total Time 9.72s (± 0.45%) 9.69s (± 0.37%) -0.03s (- 0.29%) 9.61s 9.78s
TFS - node (v10.16.3, x64)
Memory used 291,938k (± 0.02%) 291,908k (± 0.02%) -30k (- 0.01%) 291,752k 292,050k
Parse Time 0.95s (± 0.58%) 0.95s (± 0.80%) +0.00s (+ 0.32%) 0.94s 0.97s
Bind Time 0.75s (± 0.87%) 0.75s (± 0.78%) -0.00s (- 0.13%) 0.74s 0.76s
Check Time 4.29s (± 0.76%) 4.28s (± 0.57%) -0.00s (- 0.00%) 4.24s 4.34s
Emit Time 3.03s (± 1.16%) 3.04s (± 1.46%) +0.01s (+ 0.26%) 2.94s 3.15s
Total Time 9.02s (± 0.62%) 9.03s (± 0.71%) +0.01s (+ 0.12%) 8.88s 9.17s
material-ui - node (v10.16.3, x64)
Memory used 453,096k (± 0.01%) 452,846k (± 0.01%) -250k (- 0.06%) 452,737k 453,003k
Parse Time 1.78s (± 0.20%) 1.78s (± 0.45%) +0.00s (+ 0.11%) 1.76s 1.80s
Bind Time 0.68s (± 0.73%) 0.68s (± 0.91%) -0.00s (- 0.58%) 0.66s 0.69s
Check Time 13.72s (± 0.97%) 13.77s (± 0.73%) +0.05s (+ 0.33%) 13.58s 13.98s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 16.18s (± 0.82%) 16.23s (± 0.67%) +0.05s (+ 0.28%) 16.05s 16.46s
Angular - node (v12.1.0, x64)
Memory used 303,467k (± 0.02%) 302,935k (± 0.02%) -532k (- 0.18%) 302,794k 303,079k
Parse Time 1.59s (± 0.79%) 1.58s (± 0.59%) -0.01s (- 0.32%) 1.56s 1.60s
Bind Time 0.88s (± 1.30%) 0.87s (± 0.87%) -0.00s (- 0.46%) 0.86s 0.89s
Check Time 4.67s (± 0.50%) 4.64s (± 0.68%) -0.03s (- 0.62%) 4.58s 4.71s
Emit Time 5.50s (± 0.95%) 5.47s (± 0.57%) -0.04s (- 0.64%) 5.41s 5.55s
Total Time 12.64s (± 0.31%) 12.56s (± 0.41%) -0.08s (- 0.61%) 12.48s 12.68s
Monaco - node (v12.1.0, x64)
Memory used 307,015k (± 0.01%) 306,984k (± 0.02%) -31k (- 0.01%) 306,895k 307,113k
Parse Time 1.22s (± 0.76%) 1.21s (± 0.75%) -0.01s (- 0.57%) 1.19s 1.23s
Bind Time 0.74s (± 0.60%) 0.74s (± 0.66%) +0.00s (+ 0.27%) 0.74s 0.76s
Check Time 4.56s (± 0.61%) 4.56s (± 0.45%) -0.00s (- 0.07%) 4.52s 4.60s
Emit Time 2.94s (± 0.64%) 2.94s (± 0.72%) +0.01s (+ 0.27%) 2.90s 2.99s
Total Time 9.46s (± 0.33%) 9.46s (± 0.39%) +0.00s (+ 0.01%) 9.39s 9.54s
TFS - node (v12.1.0, x64)
Memory used 274,192k (± 0.02%) 274,162k (± 0.02%) -30k (- 0.01%) 274,104k 274,295k
Parse Time 0.93s (± 0.62%) 0.93s (± 0.88%) +0.00s (+ 0.00%) 0.92s 0.95s
Bind Time 0.70s (± 0.68%) 0.70s (± 1.20%) -0.00s (- 0.28%) 0.68s 0.72s
Check Time 4.19s (± 0.49%) 4.17s (± 0.47%) -0.02s (- 0.43%) 4.13s 4.21s
Emit Time 3.09s (± 1.06%) 3.07s (± 0.79%) -0.03s (- 0.84%) 3.01s 3.12s
Total Time 8.92s (± 0.47%) 8.87s (± 0.37%) -0.05s (- 0.54%) 8.79s 8.91s
material-ui - node (v12.1.0, x64)
Memory used 430,434k (± 0.05%) 430,265k (± 0.01%) -169k (- 0.04%) 430,117k 430,396k
Parse Time 1.76s (± 0.55%) 1.75s (± 0.43%) -0.00s (- 0.28%) 1.74s 1.77s
Bind Time 0.63s (± 0.95%) 0.63s (± 0.82%) +0.01s (+ 0.80%) 0.62s 0.64s
Check Time 12.19s (± 0.88%) 12.22s (± 0.72%) +0.03s (+ 0.24%) 12.03s 12.42s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 14.58s (± 0.72%) 14.60s (± 0.61%) +0.03s (+ 0.19%) 14.44s 14.81s
Angular - node (v8.9.0, x64)
Memory used 322,688k (± 0.02%) 322,212k (± 0.02%) -476k (- 0.15%) 322,100k 322,356k
Parse Time 2.11s (± 0.47%) 2.10s (± 0.19%) -0.01s (- 0.62%) 2.09s 2.11s
Bind Time 0.92s (± 0.74%) 0.92s (± 0.88%) -0.00s (- 0.43%) 0.90s 0.94s
Check Time 5.50s (± 1.39%) 5.47s (± 0.53%) -0.03s (- 0.60%) 5.42s 5.57s
Emit Time 6.21s (± 0.90%) 6.15s (± 0.94%) -0.06s (- 1.00%) 6.00s 6.29s
Total Time 14.75s (± 0.51%) 14.64s (± 0.46%) -0.11s (- 0.76%) 14.46s 14.80s
Monaco - node (v8.9.0, x64)
Memory used 325,373k (± 0.01%) 325,359k (± 0.01%) -14k (- 0.00%) 325,248k 325,410k
Parse Time 1.54s (± 0.42%) 1.54s (± 0.14%) -0.00s (- 0.19%) 1.53s 1.54s
Bind Time 0.90s (± 0.58%) 0.90s (± 0.89%) -0.00s (- 0.11%) 0.89s 0.92s
Check Time 5.39s (± 0.49%) 5.38s (± 0.39%) -0.01s (- 0.13%) 5.33s 5.42s
Emit Time 3.48s (± 0.42%) 3.48s (± 0.47%) -0.00s (- 0.09%) 3.44s 3.51s
Total Time 11.31s (± 0.25%) 11.30s (± 0.26%) -0.01s (- 0.11%) 11.21s 11.37s
TFS - node (v8.9.0, x64)
Memory used 291,335k (± 0.01%) 291,287k (± 0.02%) -48k (- 0.02%) 291,196k 291,407k
Parse Time 1.25s (± 0.46%) 1.25s (± 0.47%) +0.00s (+ 0.08%) 1.24s 1.27s
Bind Time 0.75s (± 0.49%) 0.75s (± 0.80%) +0.00s (+ 0.00%) 0.73s 0.76s
Check Time 4.87s (± 0.66%) 4.84s (± 1.42%) -0.03s (- 0.53%) 4.75s 5.10s
Emit Time 3.32s (± 1.21%) 3.32s (± 1.70%) -0.00s (- 0.12%) 3.13s 3.40s
Total Time 10.19s (± 0.35%) 10.16s (± 0.53%) -0.03s (- 0.28%) 10.04s 10.25s
material-ui - node (v8.9.0, x64)
Memory used 455,621k (± 0.01%) 455,310k (± 0.01%) -311k (- 0.07%) 455,216k 455,397k
Parse Time 2.11s (± 0.55%) 2.11s (± 0.59%) +0.01s (+ 0.38%) 2.09s 2.15s
Bind Time 0.81s (± 1.90%) 0.81s (± 0.71%) +0.00s (+ 0.49%) 0.80s 0.82s
Check Time 17.77s (± 0.57%) 17.75s (± 0.53%) -0.01s (- 0.08%) 17.50s 17.92s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 20.68s (± 0.50%) 20.68s (± 0.44%) -0.00s (- 0.02%) 20.41s 20.83s
Angular - node (v8.9.0, x86)
Memory used 185,633k (± 0.02%) 185,378k (± 0.03%) -255k (- 0.14%) 185,193k 185,468k
Parse Time 2.05s (± 0.53%) 2.05s (± 0.56%) +0.00s (+ 0.05%) 2.03s 2.07s
Bind Time 1.07s (± 1.03%) 1.07s (± 1.03%) +0.00s (+ 0.37%) 1.05s 1.11s
Check Time 5.01s (± 0.38%) 5.03s (± 0.47%) +0.02s (+ 0.44%) 4.98s 5.08s
Emit Time 6.00s (± 0.45%) 6.03s (± 1.13%) +0.03s (+ 0.55%) 5.91s 6.24s
Total Time 14.12s (± 0.23%) 14.19s (± 0.54%) +0.06s (+ 0.46%) 14.02s 14.38s
Monaco - node (v8.9.0, x86)
Memory used 185,208k (± 0.02%) 185,180k (± 0.02%) -28k (- 0.02%) 185,102k 185,253k
Parse Time 1.59s (± 0.65%) 1.59s (± 0.98%) -0.00s (- 0.06%) 1.57s 1.64s
Bind Time 0.77s (± 0.76%) 0.77s (± 0.62%) +0.00s (+ 0.00%) 0.76s 0.78s
Check Time 5.41s (± 0.46%) 5.38s (± 1.37%) -0.02s (- 0.39%) 5.12s 5.51s
Emit Time 2.87s (± 0.56%) 2.90s (± 3.28%) +0.03s (+ 1.12%) 2.79s 3.27s
Total Time 10.63s (± 0.31%) 10.64s (± 0.41%) +0.02s (+ 0.15%) 10.56s 10.74s
TFS - node (v8.9.0, x86)
Memory used 166,714k (± 0.02%) 166,703k (± 0.02%) -11k (- 0.01%) 166,649k 166,798k
Parse Time 1.28s (± 0.52%) 1.28s (± 0.53%) +0.00s (+ 0.08%) 1.27s 1.30s
Bind Time 0.72s (± 1.47%) 0.71s (± 1.06%) -0.00s (- 0.28%) 0.70s 0.73s
Check Time 4.61s (± 0.61%) 4.62s (± 0.55%) +0.01s (+ 0.11%) 4.56s 4.66s
Emit Time 3.02s (± 1.94%) 2.94s (± 1.03%) -0.08s (- 2.55%) 2.87s 3.01s
Total Time 9.63s (± 0.62%) 9.55s (± 0.39%) -0.08s (- 0.83%) 9.47s 9.64s
material-ui - node (v8.9.0, x86)
Memory used 257,680k (± 0.02%) 257,518k (± 0.01%) -162k (- 0.06%) 257,470k 257,632k
Parse Time 2.18s (± 0.42%) 2.17s (± 0.72%) -0.01s (- 0.32%) 2.14s 2.22s
Bind Time 0.69s (± 1.19%) 0.68s (± 1.33%) -0.01s (- 0.87%) 0.67s 0.71s
Check Time 16.28s (± 0.57%) 16.21s (± 0.72%) -0.07s (- 0.44%) 15.99s 16.58s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 19.15s (± 0.49%) 19.07s (± 0.64%) -0.08s (- 0.43%) 18.81s 19.44s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-166-generic
Architecturex64
Available Memory16 GB
Available Memory2 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v8.9.0, x64)
  • material-ui - node (v8.9.0, x86)
Benchmark Name Iterations
Current 37283 10
Baseline master 10

@rbuckton
Copy link
Member

rbuckton commented Dec 1, 2020

In general this looks fine, though we'd have to make a similar change to https://github.com/microsoft/tslib. One thing to note is that this does not behave the same as Chrome/v8 for this case:

class C extends Symbol.iterator {}
// v8: TypeError: Class extends value Symbol(Symbol.iterator) is not a constructor or null
// this emit: TypeError: Cannot convert a Symbol value to a string.

This could be addressed by changing the exception to this instead:

throw new TypeError("Class extends value " + String(b) + " is not a constructor or null.");

As String(Symbol.iterator) coerces symbols to strings using .toString().

@sandersn
Copy link
Member

sandersn commented Dec 2, 2020

@rbuckton should we merge this now or wait for the tslib change?

@rbuckton
Copy link
Member

rbuckton commented Dec 2, 2020

I am reviewing now. While we should still have a PR for tslib, there are no changes here that would make them incompatible.

@JoshuaKGoldberg
Copy link
Contributor Author

Super, thanks for the reviews! I can send that PR over to tslib.

@JoshuaKGoldberg JoshuaKGoldberg deleted the extends-constructor-typeerror branch December 2, 2020 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Add explicit error to __extends when base class is undefined
4 participants