2019-07 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
Stage 1 の function.sent がだいぶ放置されていたので、新たに champion を決めようという話。無事引き継がれた。
RegExp のサブクラスを与えた時にフラグを正しく受け取れない仕様バグを直す話。限定的な状況に対応すべきかで意見が分かれているっぽい。
Spec によると %TypedArray% を継承したオブジェクトを Array.prototype の __proto__ に設定した場合、新しく作られた Array に代入したときにもとの %TypedArray% の方にも代入される必要がある。
code: (js)
const o = new Int8Array(1);
Object.setPrototypeOf(Array.prototype, o);
const a = []; // a は o を継承してる
a0 = 4; // このときに o0 = 4 になるはず しかしどの実行環境でもこうはならず、o[0] は 0 のままになっている……というかどう考えても仕様バグなので直そうという話。互換性を確認することになった。
ランタイムの状態を表す [[Type]] は normal, break, continue, return, そして throw のいずれかの値を取るように規定されている。
ところで Function が持つ [[Call]] メソッドでは [[Type]] が normal か throw にしか状態が変わってほしくないが、規定されていない問題があるためちゃんと規定しようという話。コンセンサスが得られた。
code: (js)
Object.defineProperty(Object.prototype, '2', {
get() { print('get'); return 4; },
set(v) { print(set with ${v}); }
});
array.sort();
console.log(array);
のようなコードを実行した際に
code: (console)
$ eshost -s test.js
#### Chakra, SpiderMonkey, V8, XS
get
set with 3
1,2,hole,4,undefined,undefined,hole
#### JavaScriptCore
get
set with 2
get
get
get
TypeError: undefined is not an object
と環境によって異なる結果になってしまっているため Array#sort をもっと厳格にする話。
Stage 4 (ES2020)
現行の Dynamic Imports の仕様だと二回目以降の読み込みでは初回時の読み込みと同じオブジェクトを返すようになっている。
読み込みに失敗した場合にも同じオブジェクトを返すので、このままだと読み込みに失敗したら再度読み込みたいときには URL に hash を仕込むみたいなハックが必要になりだいぶ困るので、成功した場合にのみキャッシュすることになった。
全てのモダンな実装に入り、無事 Stage 4 になった 🎉
Stage 3
正規表現のメソッド(RegExp#exec, String#{replace, match, matchAll})を実行したときに、返り値で capture groups についての情報を取ることが出来ないため、メソッドを拡張する提案。
以前は RegExp#exec(target: string, mapfn?: (start: number, end: number, input: string) => string) のように新たに mapfn を追加して対応する提案だったが、ここが options となり { capture?: "indices" | "strings" } を受け取るように変更された。
code: (js)
const re = /a+(?<Z>z)?/
const m = re.exec("xaaaz")
const m2 = re.exec("xaaaz", { capture: "indices" })
console.log(m2); // [1, 5, 4, 5, index: 1, input: "xaaaz", groups: {Z: 4. 5}] Stage 3 になった。
|| や && との優先度については括弧を使って明示的に指定しないと SyntaxError にすることで落ち着いたらしい。Stage 3 になった。
Stage 3 になった。
Stage 2
String#replace と違って第一引数に入れた文字列にマッチした部分を全て置換するやつ。ほとんど JIT 最適化目的。
今までは第一引数に RegExp を突っ込んだら TypeError にすることにしていたが、RegExp が来たときに無理矢理 /g フラグが付いてるかのように扱うなどの提案も出ている。
/g フラグについては互換性の問題も出ており、それを解決するまでは Stage 3 にはならないとのこと。 Collection で正規化できるようにする提案。DOM API では URLSearchParams, Element#dataset の DomStringMap, そして Headers のような事例がある。
函数の実装を隠す提案。セキュリティのため Function#toString や Stacktraces に載せないといった効果がある。
いままで出た Features をまとめると
A
スタックトレースに表示する
¬A
スタックトレースに表示しない
B
スタックトレースで函数のコード上の位置を表示しない
あたかも NativeFunction のように見せる(polyfill のため)
C
Function#length を函数宣言時の引数の状態から設定しない
Function#name を Syntactic Binding によって設定しない
といったものが挙げられていた。この提案では新たに ¬A と B を対応することを考えることになった。
対応する方法として
API
¬A: Error.hideFromStackTraces(f) と B: Error.hideImplementationDetails(f) を用意する
シンタックスの変更を要しない
静的解析が困難
シュレディンガーディレクティブ
export function で書かれた場合問答無用でスタックトレースから消す
だいたい上手くいくが不親切
付加ディレクティブ
函数の初めに "sensitive"; や "hide implementation"; を入れる
もしくは "hide from stack traces"; と "hide implementation";
チェイン
"hide implementation"; から呼び出された函数は全て sensitive 扱いにする
隠したい場合に函数をラップする必要が出てくる
次回 Stage 3 にするべく動いていくらしい。
Error のプロパティは基本的に enumerable になっていないが、AggregateError#errors を enumerable にするかどうかが議論されている。Stage 2 になった。
Iterator インターフェースは next メソッド(とオプショナルで return メソッド)しか要求されないため map, filter といった便利なメソッドが使えない問題がある。それを解決するために ECMAScript で定義されている ArrayIterator や Generator Functions で作られた Iterator といったビルトインな Iterator の実装については追加でメソッドを付与する。
追加されるメソッドについては uhyo さんがまとめている。
更に独自実装で Iterator を作る場合には Iterator.syncPrototype を継承すればこれらのメソッドが使えるようになる。Stage 2 になった。
すぐにリソースを開放する必要がある場合に、開放し忘れないように新しいシンタックスを用意する提案。例えば ECMAScript における Iterator?#return() や WHATWG Streams API における Reader#releaseLock() などがこれにあたる。 新たに Symbol.dispose と Symbol.asyncDispose メソッドを用意し、新しいシンタックスの通りに記述すると自動でこれらが呼ばれるような仕様になっている。
以前は using キーワードを新たに作って提案されていたが、try を使い回すようなものに変更されている。
code: (js)
try (const a = expr, b = expr) {
} catch (e) {
// try の中のエラーや @@dispose が呼ばれたことによる例外がここに来る
// もし @@dispose で複数の例外が発生したら AggregateError にすることが提案されている
}
Stage 2 になったが、シンタックスについては引き続き考慮することになった。
uhyo さんの記事が詳しい。
Stage 1
前回に引き続き Content Security Policy の Trusted Types のための提案。Stage 2 にはならなかったみたい。
こっちも Trusted Types のための提案。Stage 1 になった。
Built-in Modules として js: を基本として、各プラットフォームで web:, node: や device: を使うのはどうかという話がされた。
Symbol.reverse and related methods for Array, Map, and Set for stage 1 (link in progress, slides) Iterator を生成できるメソッドに keys(), values() そして entries() があるように [@@reverse]()によって逆向きの Iterator を得られるようにする提案。すでにある Array#reverse() と合流させていいのではという意見もある。Stage 1 になった。
Map において、存在しない場合に挿入する処理は
code: (js)
if (!map.has(key)) {
map.set(value);
}
と2回の loopups が必要になり、存在する場合に更新する処理は
code: (js)
if (map.has(key)) {
const oldValue = map.get(key);
const newValue = ...; // oldValue を使って新しい値を作る
map.set(newValue);
}
と3回の loopups を要する。ここで Map#updateOrInsert(key, (oldValue) => newValue, () => value) を用意することで無駄な loopups をなくそうという提案。Stage 1 になった。
Stage 0
かつて concurrency の提案がなされて時の ! 演算子について再考する話。await とは違い ! を使った場合、tick を発生させずに Promise の結果を受け取ることができるらしい。モチベーションがなさそう。
その他
Array.from と %TypedArray%.from において第二引数を入れたときの実行順が異なる。
Array.from の場合は第一引数を一つ取り出しては map する函数を呼ぶ。
code: (js)
function* g() {
for (let i = 1; i < 5; ++i) {
console.log("iterate", i);
yield i;
}
console.log('done');
}
Array.from(g(), (i) => {
console.log("map", i);
return i * 2;
});
/*
iterate 1
map 1
iterate 2
map 2
iterate 3
map 3
iterate 4
map 4
done
*/
%TypedArray%.from の場合は先に第一引数を全て取り出してしまった後に map するようになっている。
code: (js)
function* g() {
for (let i = 1; i < 5; ++i) {
console.log("iterate", i);
yield i;
}
console.log('done');
}
Uint8Array.from(g(), (i) => {
console.log("map", i);
return i * 2;
});
/*
iterate 1
iterate 2
iterate 3
iterate 4
done
map 1
map 2
map 3
map 4
*/
%TypedArray%.from の方を修正するという話になりそう。
Built-in Modules において細かな仕様をどうするか。今のところ
Enumerability
Same-realm Brand Checks
Freezing
について話されているっぽい。
Intl.DisplayNames のオプションは kebab-case か camelCase のどっちがよいかの話。今のところ camelCase になっているが、W3C Style Guide によると enum のようなオプションの場合は kebab-case を使うことになっている。
総括
今回は Promise.allSettled が Stage 4 になり、 Nullish Coalescing と Optional Chaining が Stage 3 に上がり、Babel などを使って実用的にこれらが扱えるようになって来たのではないかと思う。Built-in Modules が面白そうなので決着がついてほしいところではある。