Skip to content

Commit

Permalink
fix #47: implement lowering for private members
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 21, 2020
1 parent 0bc5da8 commit 07b1218
Show file tree
Hide file tree
Showing 11 changed files with 2,071 additions and 142 deletions.
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

## Unreleased

* Transform private names ([#47](https://github.com/evanw/esbuild/issues/47))

Private names are an access control mechanism for classes. They begin with a `#` and are not accessible outside of the class they are declared in. Support for parsing this syntax was added in esbuild version 0.4.9 but the syntax was passed through unmodified, meaning it didn't work in older browsers.

This release adds support for transforming private fields and private methods for older browsers that don't support this syntax. This transform uses `WeakMap` and `WeakSet` to preserve the privacy properties of this feature, similar to the corresponding transforms in the Babel and TypeScript compilers.

This code:

```js
class Counter {
#count = 1
get value() { return this.#count }
increment() { ++this.#count }
}
```

is transformed to this code when using `--target=es2020`:

```js
var _count;
class Counter {
constructor() { _count.set(this, 1); }
get value() { return __privateGet(this, _count); }
increment() { __privateSet(this, _count, +__privateGet(this, _count) + 1); }
}
_count = new WeakMap();
```

Note that as far as I know, most modern JavaScript engines (V8, JavaScriptCore, and SpiderMonkey but not ChakraCore) may not have good performance characteristics for large `WeakMap` and `WeakSet` objects. Creating many instances of classes with private fields or private methods with this syntax transform active may cause a lot of overhead for the garbage collector. This is because engines other than ChakraCore store weak values in an actual map object instead of as hidden properties on the keys themselves, and large map objects can cause performance issues with garbage collection.

* Fix re-exports when bundling

This is similar to the fix for re-exports in version 0.5.6 except that it applies when bundling, instead of just when transforming. It needed to be fixed differently because of how cross-file linking works when bundling.
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,20 @@ These syntax features are always transformed for older browsers:

These syntax features are conditionally transformed for older browsers depending on the configured language target:

| Syntax transform | Transformed when `--target` is below | Example |
|-------------------------------------------------------------------------------------|--------------------------------------|----------------------|
| [Exponentiation operator](https://github.com/tc39/proposal-exponentiation-operator) | `es2016` | `a ** b` |
| [Spread properties](https://github.com/tc39/proposal-object-rest-spread) | `es2018` | `let x = {...y}` |
| [Optional catch binding](https://github.com/tc39/proposal-optional-catch-binding) | `es2019` | `try {} catch {}` |
| [Optional chaining](https://github.com/tc39/proposal-optional-chaining) | `es2020` | `a?.b` |
| [Nullish coalescing](https://github.com/tc39/proposal-nullish-coalescing) | `es2020` | `a ?? b` |
| [Class instance fields](https://github.com/tc39/proposal-class-fields) | `esnext` | `class { x }` |
| [Static class fields](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static x }` |
| [Logical assignment operators](https://github.com/tc39/proposal-logical-assignment) | `esnext` | `a ??= b` |
| Syntax transform | Transformed when `--target` is below | Example |
|-------------------------------------------------------------------------------------|--------------------------------------|----------------------------|
| [Exponentiation operator](https://github.com/tc39/proposal-exponentiation-operator) | `es2016` | `a ** b` |
| [Spread properties](https://github.com/tc39/proposal-object-rest-spread) | `es2018` | `let x = {...y}` |
| [Optional catch binding](https://github.com/tc39/proposal-optional-catch-binding) | `es2019` | `try {} catch {}` |
| [Optional chaining](https://github.com/tc39/proposal-optional-chaining) | `es2020` | `a?.b` |
| [Nullish coalescing](https://github.com/tc39/proposal-nullish-coalescing) | `es2020` | `a ?? b` |
| [Class instance fields](https://github.com/tc39/proposal-class-fields) | `esnext` | `class { x }` |
| [Static class fields](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static x }` |
| [Private instance methods](https://github.com/tc39/proposal-private-methods) | `esnext` | `class { #x() {} }` |
| [Private instance fields](https://github.com/tc39/proposal-class-fields) | `esnext` | `class { #x }` |
| [Private static methods](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static #x() {} }` |
| [Private static fields](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static #x }` |
| [Logical assignment operators](https://github.com/tc39/proposal-logical-assignment) | `esnext` | `a ??= b` |

These syntax features are currently always passed through un-transformed:

Expand All @@ -118,10 +122,6 @@ These syntax features are currently always passed through un-transformed:
| [Async generators](https://github.com/tc39/proposal-async-iteration) | `es2018` | `async function* foo() {}` |
| [BigInt](https://github.com/tc39/proposal-bigint) | `es2020` | `123n` |
| [Hashbang grammar](https://github.com/tc39/proposal-hashbang) | `esnext` | `#!/usr/bin/env node` |
| [Private instance methods](https://github.com/tc39/proposal-private-methods) | `esnext` | `class { #x() {} }` |
| [Private instance fields](https://github.com/tc39/proposal-class-fields) | `esnext` | `class { #x }` |
| [Private static methods](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static #x() {} }` |
| [Private static fields](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static #x }` |

These syntax features are not yet supported, and currently cannot be parsed:

Expand Down
5 changes: 3 additions & 2 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,8 @@ const (
SymbolClass

// A class-private identifier (i.e. "#foo").
SymbolPrivate
SymbolPrivateField
SymbolPrivateMethod
SymbolPrivateGet
SymbolPrivateSet
SymbolPrivateGetSetPair
Expand All @@ -931,7 +932,7 @@ const (
)

func (kind SymbolKind) IsPrivate() bool {
return kind >= SymbolPrivate && kind <= SymbolPrivateGetSetPair
return kind >= SymbolPrivateField && kind <= SymbolPrivateGetSetPair
}

func (kind SymbolKind) IsHoisted() bool {
Expand Down
Loading

0 comments on commit 07b1218

Please sign in to comment.