Skip to content

Commit

Permalink
fix #2365, close #2372: bug in compat-table code
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 6, 2022
1 parent 8b5e048 commit 18ef7be
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 20 deletions.
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,36 @@

This fix was contributed by [@magic-akari](https://github.com/magic-akari).

* Fix a bug in esbuild's feature compatibility table generator ([#2365](https://github.com/evanw/esbuild/issues/2365))

Passing specific JavaScript engines to esbuild's `--target` flag restricts esbuild to only using JavaScript features that are supported on those engines in the output files that esbuild generates. The data for this feature is automatically derived from this compatibility table with a script: https://kangax.github.io/compat-table/.

However, the script had a bug that could incorrectly consider a JavaScript syntax feature to be supported in a given engine even when it doesn't actually work in that engine. Specifically this bug happened when a certain aspect of JavaScript syntax has always worked incorrectly in that engine and the bug in that engine has never been fixed. This situation hasn't really come up before because previously esbuild pretty much only targeted JavaScript engines that always fix their bugs, but the two new JavaScript engines that were added in the previous release ([Hermes](https://hermesengine.dev/) and [Rhino](https://github.com/mozilla/rhino)) have many aspects of the JavaScript specification that have never been implemented, and may never be implemented. For example, the `let` and `const` keywords are not implemented correctly in those engines.

With this release, esbuild's compatibility table generator script has been fixed and as a result, esbuild will now correctly consider a JavaScript syntax feature to be unsupported in a given engine if there is some aspect of that syntax that is broken in all known versions of that engine. This means that the following JavaScript syntax features are no longer considered to be supported by these engines (represented using esbuild's internal names for these syntax features):

Hermes:
- `arrow`
- `const-and-let`
- `default-argument`
- `generator`
- `optional-catch-binding`
- `optional-chain`
- `rest-argument`
- `template-literal`

Rhino:
- `arrow`
- `const-and-let`
- `destructuring`
- `for-of`
- `generator`
- `object-extensions`
- `template-literal`

IE:
- `const-and-let`

## 0.14.48

* Enable using esbuild in Deno via WebAssembly ([#2323](https://github.com/evanw/esbuild/issues/2323))
Expand Down
16 changes: 0 additions & 16 deletions internal/compat/js_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,9 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{13, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{45, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 0, 0}}},
Opera: {{start: v{36, 0, 0}}},
Rhino: {{start: v{1, 7, 13}}},
Safari: {{start: v{10, 0, 0}}},
},
AsyncAwait: {
Expand Down Expand Up @@ -392,20 +390,16 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{14, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{51, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IE: {{start: v{11, 0, 0}}},
IOS: {{start: v{11, 0, 0}}},
Node: {{start: v{6, 0, 0}}},
Opera: {{start: v{36, 0, 0}}},
Rhino: {{start: v{1, 7, 13}}},
Safari: {{start: v{11, 0, 0}}},
},
DefaultArgument: {
Chrome: {{start: v{49, 0, 0}}},
Edge: {{start: v{14, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{53, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 0, 0}}},
Opera: {{start: v{36, 0, 0}}},
Expand All @@ -420,7 +414,6 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 5, 0}}},
Opera: {{start: v{38, 0, 0}}},
Rhino: {{start: v{1, 7, 14}}},
Safari: {{start: v{10, 0, 0}}},
},
DynamicImport: {
Expand Down Expand Up @@ -472,19 +465,16 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 5, 0}}},
Opera: {{start: v{38, 0, 0}}},
Rhino: {{start: v{1, 7, 13}}},
Safari: {{start: v{10, 0, 0}}},
},
Generator: {
Chrome: {{start: v{50, 0, 0}}},
Edge: {{start: v{13, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{53, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 0, 0}}},
Opera: {{start: v{37, 0, 0}}},
Rhino: {{start: v{1, 7, 13}}},
Safari: {{start: v{10, 0, 0}}},
},
Hashbang: {
Expand Down Expand Up @@ -582,7 +572,6 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{4, 0, 0}}},
Opera: {{start: v{31, 0, 0}}},
Rhino: {{start: v{1, 7, 14}}},
Safari: {{start: v{10, 0, 0}}},
},
ObjectRestSpread: {
Expand All @@ -601,7 +590,6 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{79, 0, 0}}},
ES: {{start: v{2019, 0, 0}}},
Firefox: {{start: v{58, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{11, 3, 0}}},
Node: {{start: v{10, 0, 0}}},
Opera: {{start: v{53, 0, 0}}},
Expand All @@ -612,7 +600,6 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{91, 0, 0}}},
ES: {{start: v{2020, 0, 0}}},
Firefox: {{start: v{74, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{13, 4, 0}}},
Node: {{start: v{16, 9, 0}}},
Opera: {{start: v{77, 0, 0}}},
Expand Down Expand Up @@ -683,7 +670,6 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{12, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{43, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{10, 0, 0}}},
Node: {{start: v{6, 0, 0}}},
Opera: {{start: v{34, 0, 0}}},
Expand All @@ -694,11 +680,9 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
Edge: {{start: v{13, 0, 0}}},
ES: {{start: v{2015, 0, 0}}},
Firefox: {{start: v{34, 0, 0}}},
Hermes: {{start: v{0, 7, 0}}},
IOS: {{start: v{9, 0, 0}}},
Node: {{start: v{4, 0, 0}}},
Opera: {{start: v{28, 0, 0}}},
Rhino: {{start: v{1, 7, 14}}},
Safari: {{start: v{9, 0, 0}}},
},
TopLevelAwait: {
Expand Down
33 changes: 29 additions & 4 deletions scripts/compat-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ const engines = [
'rhino',
]

function getValueOfTest(value) {
// Handle values like this:
//
// {
// val: true,
// note_id: "ff-shorthand-methods",
// ...
// }
//
if (typeof value === 'object' && value !== null) {
return value.val === true
}

// String values such as "flagged" are considered to be false
return value === true
}

function mergeVersions(target, res) {
// The original data set will contain something like "chrome44: true" for a
// given feature. And the interpolation script will expand this to something
Expand All @@ -109,7 +126,7 @@ function mergeVersions(target, res) {
const lowestVersionMap = {}

for (const key in res) {
if (res[key] === true) {
if (getValueOfTest(res[key])) {
const match = /^([a-z_]+)[0-9_]+$/.exec(key)
if (match) {
const engine = match[1]
Expand Down Expand Up @@ -316,9 +333,17 @@ for (const test of [...es5.tests, ...es6.tests, ...stage4.tests, ...stage1to3.te
if (feature) {
feature.found = true
if (test.subtests) {
for (const subtest of test.subtests) {
mergeVersions(feature.target, subtest.res)
}
const res = {}

// Intersect all subtests (so a key is only true if it's true in all subtests)
for (const subtest of test.subtests)
for (const key in subtest.res)
res[key] = true
for (const subtest of test.subtests)
for (const key in res)
res[key] &&= getValueOfTest(subtest.res[key] ?? false)

mergeVersions(feature.target, res)
} else {
mergeVersions(feature.target, test.res)
}
Expand Down

0 comments on commit 18ef7be

Please sign in to comment.