2023-05 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
needs consensus: #3052 limit ArrayBuffer size to $ 2^{53} bytes (slides) | Michael Ficarra Array の最大の長さは $ 2^{32}-1となっている。一方で ArrayBuffer は仕様で最大の長さが明示的に定められていない状態だった(既知の問題だったが長いこと放置されていた)。この変更でその最大値が Number.MAX_SAFE_INTEGER である $ 2^{53}-1 に定められた。
これによって V8 12.0 から ArrayBuffer の最大サイズが$ 2^{32}から$ 2^{53}-1になった。もちろんそれ以下のサイズであってもアロケーションに失敗して RaneError を投げる可能性はある。
Stage 4 (ES2024)
HTML で言うところの USVString を扱う提案。Stage 4 になった 🎉
Atimics.wait の非同期版。Stage 4 になった 🎉
正規表現に v (unicodeSets) フラグを追加する提案。詳しくは記事に書いてある。Stage 4 になった 🎉 Stage 3
渡すオブジェクトによってはコンストラクタが2回呼ばれてしまう問題があったのが修正された。
Iterator Helpers: should Symbol.iterator fallback be a callable check or an undefined/null check? (slides) | Michael Ficarra ECMA262 では Iterable かどうかのチェックを Symbol.iterator プロパティが undefined/null でないかどうかで判断している。一方で現状の Iterator Helpers は Symbol.iterator が isCallable かどうかでチェックをしていて整合性がない状態となっている。
ECMA262 の現状の仕様にあわせて Iterator Helpers でも undefined/null でチェックするようになった。
Iterator Helpers: should malformed iterators fail early or fail only when iterated? (slides) | Michael Ficarra Iterator の "next" メソッドが正しくない場合にどのタイミングで例外を投げるべきか議論された。
https://gyazo.com/1449578b05270c2f2fbb66866405f5c9
Option 3 が選ばれ、Iterator Helpers メソッドでは "next" メソッドはバリデートされず、値を取得しようとしたときに例外が投げられることとなった。
前回の会議において各実装で特に問題が見つからなかったら先に進めると告知されていた。各エンジンから特に懸念が出されることなくそのまま Stage 3 になった。
binary16 形式はあくまで現状 GPU に送るための形式であり計算用ではないため binary32, binary64 からのキャスト以外では最適化されないだろうとのこと。わかる。
Decorators でメタデータを追加できるようにする提案。Function.prototype[Symbol.metadata] を non-configurable かつ non-writable な null として定義し、Decorators を持たないクラスは @@metadata プロパティを持たないようにする方針になった。Stage 3 になった。
いくつかの細かい Normative Change があった。次回の会議で Stage 4 にすべく進める予定。
polyfill 作者である Adam Shaw 氏から Duration#round のバグが報告されて修正された。
code: js
const earlier = new Temporal.PlainDate(2022, 1, 1);
const later = new Temporal.PlainDate(2023, 12, 25);
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
console.log(duration.toString());
// Current spec text: 1Y12M
// After this change: 2Y
また IETF からのレビューでカレンダーを複数指定出来るようになった。複数指定されたとき先頭から順にユーザーエージェントが扱えるカレンダーが選択される。なお複数指定ではクリティカルフラグ ! を使うと例外を投げる。
code: js
// Current spec text: Throws a RangeError
// After this change: Creates a PlainYearMonth object with ISO 8601 calendar
// Current spec text: Created a PlainDate object with ISO 8601 calendar
// After this change: Throws a RangeError
他のも Mozilla からのフィードバックでパフォーマンスのために一部の仕様が修正されている。
Stage 3 になっているものの、仕様の細かい設計やトレードオフについて議論がされた。Stage 3 に留まったが今後 Normative Change を行いそう。
Stage 2
Uint8Array に Base64 や Hex 用のメソッドを追加する提案。PartialBase64 はストリーム用となっている。
code: ts
interface Base64Options {
alphabet?: "base64" | "base64url" | undefined;
}
interface FromPartialBase64Options extends Base64Options {
more?: boolean;
extra?: string;
}
interface ToPartialBase64Options extends Base64Options {
more?: boolean;
extra?: Uint8Array;
}
interface FromPartialBase64Result {
result: Uint8Array;
extra: string | null;
}
interface ToPartialBase64Result {
result: string;
extra: Uint8Array | null;
}
declare class Uint8Array {
static fromBase64(str: string, options?: Base64Options): Uint8Array;
static fromPartialBase64(str: string, options?: FromPartialBase64Options): FromPartialBase64Result;
static fromHex(str: string): Uint8Array;
toBase64(options?: Base64Options): string;
toPartialBase64(options?: ToPartialBase64Options): ToPartialBase64Result;
toHex(): string;
}
Stage 2 になった。Node.js の Buffer が Uint8Array を継承している関係で、ちょっと困惑させちゃいそうだなと思う。
非 Symbol を渡したときに例外を投げるべきかどうかが議論された。結果的にメソッド名を Array.isArray に倣って Symbol.is{WellKnown, Registered}Symbol と改め、例外は投げないことになった。
Decorator field/accessor initializer order (issue, slides) | Chris Hewell Garrett Decorators の提案によって accessor に新しいキーワードが追加されることになっている。
code: js
function appendString(str, { kind }) {
if (kind !== "accessor") {
throw new Error("これはアクセサデコレーターです!");
}
return function ({ set }) {
return {
// インスタンスの Define のフック
init(initVal) {
return ${initVal}${str};
},
// インスタンスの Set のフック
set(val) {
return set.call(this, ${val}${str});
},
};
};
}
class Foo {
@appendString("bar")
accessor foo = "foo";
}
const instance = new Foo();
console.log(instance.foo); // => "foobar";
instance.foo = "foofoo";
console.log(instance.foo); // "foofoobar";
さて今の仕様だと Decorators の適用順は全て内側から外側になるように定義されているが、getter/setter/method については関数をラップする関係上外側から内側に効果が適用されることになる。init との一貫性がない。
code: js
class Foo {
@appendString("baz")
@appendString("bar")
accessor foo = "foo";
}
const instance = new Foo();
console.log(instance.foo); // => "foobarbaz";
instance.foo = "foo";
console.log(instance.foo); // "foobazbar";
議論の末 Accessor Decorators の init に限って外側から内側に実行されることになった。
Promise.withResolvers for stage 2 (repo, slides) | Peter Klecha 現状 Promise の resolve, reject 函数をコンストラクタ函数の外に出すのに手間がかかる。
code: js
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
これを解決するために Promise.withResolvers を導入する提案。
code: js
const { promise, resolve, reject } = Promise.withResolvers();
Stage 2 になった。
ところで Rollup や Svelte で有名な Rich Harris 氏が resolve ではなく fulfil にするように提言していた。
const { promise, fulfil, reject } = Promise.withResolvers()
i will die on this hill
https://pbs.twimg.com/media/FwXNcChWwAIzi6u.jpg
@Rich_Harris: @robpalmer2 @buildsghost obviously that ship has long since sailed, because of decisions made by smarter people than me. but i do wonder if we missed an opportunity to make some of this stuff a bit less magical and a bit more self-explanatory このあたりの用語は難しい……。
Dynamic Imports においてフェーズを指定するシンタックスが変わった。
code: js
// before
const module = await import("./foo.wasm", { phase: "source" });
// after
const module = await import.source("./foo.wasm");
実質 WebAssembly のための提案だが、広範な提案であり十分定義がされていないということで Stage 3 にはならなかった。次回会議で Stage 3 を目指すとのこと。
結果 {Object, Map}.groupBy に変更された。Stage 3 だったがやり直すため Stage 2 に降格した。
タイムゾーンについて各エンジンで差異が生まれてしまっている。また現状の仕様だと Europe/Kyiv ⇨ Europe/Kiev のようにユーザーの意図せずに変更をしてしまう。
仕様の中で TZDB ID について記載し、Temporal.TimeZone#equals ではその ID を元に等しいかを判定する提案。Stage 2 になった。
今まで AsyncContext クラスで扱っていたものを AsyncLocal もしくは AsyncVariable に、AsyncContext.wrap は AsyncSnapshot クラスに変更するとのこと。またそれらを AsyncContext ネームスペースで提供する。
その他にコンストラクタ周りにも変更があった。
Stage 1
Temporal.ZonedDateTime に対応した Intl.ZenedDateTimeFormat の提案。Stage 1 になった。
その他
現状 Source maps の仕様は Google によって Google Docs で定義されている。これを TC39 管理に移譲しないかどうかの議論。
TG4 を結成し標準化を進めることとなった。
Module Harmony: interaction semantics of the different proposals (slides) | Nicolò Ribaudo モジュール周りの提案の整理。
https://gyazo.com/9162330249b47618055ab42637ee6000
Module Harmony Epic リポジトリを立ち上げるとのこと。
Process: Add Implementation Status to Proposal Pages for approval (slides) | Michael Saboff Stage 3 になった提案を実装するにあたって、各エンジンの実装ステータスを共有する場所が欲しいという話。とりあえずテーブルにその情報が追加されることとなった。
総括
ES2023 に入れる提案が確定し、今回から ES2024 になる。
Stage 4 になったものが多く特に RegExp の v フラグが便利そう。個人的には polyfill を書いている Float16Array がようやく Stage 3 になりかなり嬉しい。
その他の提案では Stage 2 Base64 の提案で Uint8Array に独自メソッドを追加するのが気になった。今後は Node.js の Buffer のようにバイナリ列を扱う場合は ArrayBuffer ではなく Uint8Array を積極的にインターフェースとして使うことになるかもしれないと思った。