2019-06 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
eval は非文字列を受け取ったとき、ただその中身を返すだけの仕様になっている。
ところで Content Security Policy などによって eval による文字列からの実行を拒絶することが出来る(HostEnsureCanCompileStrings)が、これが引数の型チェック以前に行ってしまっているため、非文字列を渡したときも拒絶されてしまうようになってしまっている。実際に Firefox 66 において eval(0) が拒絶されてしまっている。
これを修正するために先に文字列かどうかの型チェックをして、その後で HostEnsureCanCompileStrings を呼び出すようにする提案。コンセンサスが得られた。
ES2016 的には anonymous functions には name プロパティが付いていない。
code: (js)
(() => {}).hasOwnProperty("name") // false
しかし実際の実装としてはそうはなっておらず、V8, SpiderMonkey, JavaScriptCore はプロパティがついており、ChakraCore は __proto__ が持つようになっている。Web Reality のためにプロパティをつけようという話。
また同様に anonymous class については V8, SpiderMonkey, ChakraCore が __proto__ で持ち、JavaScriptCore がプロパティとして持つようになっているため、これも Web Compatible のためにプロパティで持つようにしたらよいのではないかという話も出ている。コンセンサスが得られた。
以前のイテレーターの "next" メソッドや、議論中の Set のコンストラクタ内の "add" の呼び出し同様に、Promise.{All, Race} 内における "resolve" の呼び出しをキャッシュ化するやつ。コンセンサスが得られた。
仕様上で early errors が ReferenceError と誤って記載されているのを SyntaxError と修正するやつ。コンセンサスが得られた。
Intl.DataTimeFormat にオプションを追加する話。既存の API を壊さず、小さい変更なため Stage ではなくサクッと入れる感じらしい(ただし Stage 4 の要件を満たす必要がある)。
Stage 4 (ES2020)
予定通り Dynamic Imports が Stage 4 になった 🎉
Chrome 63, Safari 11.1, Firefox 67 に実装されている。
Editor Review が終わり次第 Stage 4 になる。
Chrome 67, Firefox 68 に実装されている。
Stage 3
Stage 3 になった。
Dynamic Imports 周りとの競合の問題が起きていたが、その辺りは解決したのだろうか。
Stage 2
今まで for-in の順番はは実装依存だったがメジャーな実装で同じ順にイテレートするようになっていたため、実装から仕様に落とし込む提案。無事 Stage 2 になった。
for-in 自体は非 Symbol で enumerable なキーを __proto__ をも含めてイテレートするやつ。
Stage 2 になった。
Stage 1 からのアップデートとして
of メソッドがシンプルになった
type オプションが追加され、新たに "currency", "dataField", "dataSymbol" の 3 つのタイプをサポートするようになった
fallback オプションが追加された
currency は a well-formed 3-letter ISO 4217 に対しての文字列が得られる。
code: (js)
// Display names in English
const currencyNames = new Intl.DisplayNames('en', { type: 'currency' }); currencyNames.of('USD'); // => "US Dollar"
currencyNames.of('TWD'); // => "New Taiwan Dollar"
currencyNames.of('EUR'); // => "Euro"
datafield は “era”, “year”, “quarter”, “month”, “weekOfYear”, “weekday”, “day”, “dayPeriod”, “hour”, “minute”, “second”, “zone” に対しての文字列が得られる。
code: (js)
const fieldNames = new Intl.DisplayNames('en', { type: 'dateField' }); fieldNames.of('year'); // => "year"
fieldNames.of('weekOfYear'); // => "week"
fieldNames.of('weekday'); // => "day of the week"
fieldNames.of('dayPeriod'); // => "AM/PM"
fieldNames.of('zone'); // => "time zone"
dataSymbol は “sunday”, “monday”, “tuesday”, “wednesday”, “thursday”, “friday”, “saturday”, “january”, “february”, “march”, “april”, “may”, “june”, “july”, “august”, “september”, “october”, “november”, “december”, “q1”, “q2”, “q3”, “q4”, “am”, “pm” に対しての文字列が得られる。
code: (js)
const symbolNames = new Intl.DisplayNames('en', { type: 'dateSymbol' }); symbolNames.of('saturday'); // => "Saturday"
symbolNames.of('september'); // => "September"
symbolNames.of('q1'); // => "1st quarter"
symbolNames.of('pm'); // => "PM"
fallback オプションについては "code" (default) と "none" を指定することが出来、該当する文字列が得られない時に前者だと入力した文字列をそのまま返し、後者だと undefined を返すようになる。
セキュリティの観点から Tagged Template で作られた Frozen Array かどうかを boolean で取得できるようにするメソッド。Cross Realms の場合は問答無用で false を返すらしい。Stage 2 になった。
Optional Call ではちゃんと this を考慮して呼ばれるようになっている。
code: (js)
a.b?.();
// Equivalent
(_b = a.b) == null ? undefined : _b.Call(a);
また今後の予定としてコンセンサスが得られたら delete 演算子の対応を取り消すとのこと。Stage 2 になった。
?? 演算子の優先度をどうするかが問題となっている。案としては以下の通り。
|| と同じ優先度にする
今まで falsy について考えなくていい場合は || を書いていたところを何も考えずに ?? に置き換えていくことが出来る
?? か || よりも先に実行されるようにする
明示的に優先度を指定しないといけなくする
Exponentiation Operator が -1 ** 2 を拒否するみたいなやつ
Stage 1
Promise[] や Iteratable, AsyncIterable (Pull-based Steams) をすべてまとめて Push-based Streams で扱うことが出来る Emitter の提案。DOM API の EventTarget や Node.js の EventEmitter と後方互換性があり、標準の実装に入れることでいかなる JavaScript のライブラリを使ったときよりも速くなる。
code: (js)
const emitter = new Emitter();
emitter.each(d => console.log('d', d)); // d 42
emitter.next(42);
// only send true if an odd number was received
new Emitter((d, i, { send }) => { if (d % 2) send(true) });
// delay sending the value by 1 second
new Emitter((d, i, { send }) => { setTimeout(() => send(d), 1000) });
// send 3 values, then resolve the emitter
new Emitter((d, i, { send, resolve, reject }) => {
send(1);
send(2);
send(3);
resolve('done');
});
code: (js)
/** @file 配列を初期化する例 */
const { map, filter, run, reduce } = Emitter;
function* range(from, to) {
for (let i = from; i <= to; i++) {
yield i;
}
}
const arr = run(
range(40, 60),
map(d => d / 4),
filter(d => d < 12),
reduce([]), // Emitter.reduce でシリアライズしなおす
);
code: (js)
/** @file document 内の HTMLButtonElement がクリックされたときの座標を console.log する例 */
const { on, map, filter, run } = Emitter;
run(
on(document, 'click'),
filter(ev => ev.target.tagName === 'BUTTON'),
map(ev => ({ x: ev.clientX, y: ev.clientY })),
coords => console.log(coords),
);
Emitter.prototype として
each
next
resolve
reject
Static Helpers として
from
run
on
emit
そして Operators として
map
filter
reduce
flatten
until
を持つ。
Stage 1 Observables との比較として
どっちも Push-based Streams である
Observable は Single-Consumer だが Emitter は Multiple Consumers をサポートする
Observable は双方向な signals を持つが、Emitter は推論しやすくするために単方向を保証する
Observable は Observable#subscribe する度に Subscription を作りストリームを流すため、その度に Emitter のようなものを作っていると言える。つまりより高い抽象度を持つ
Stage 1 になった。フィードバックを得ていくとのこと。ざっくりとした使い方は Walkthrough を見ればよさそう。 Evalable については今まで eval に非文字列を入れた場合は何もせずそのまま返すようにしていたが @@evalable が true な Object については eval 可能として扱うようにする提案。
Host compile value adjustment については、今まで eval や CreateDynamicFunction で呼ばれる HostEnsureCanCompileStrings という実装依存のチェックには Realms しか渡ってこないようになっていた。これを HostBeforeCompileValue として最終的に求めるもの(各種 Function なのか Script なのか)と引数に入れられた Evalable なオブジェクト(もしくは単に文字列)についてもチェックできるようにした。
Stage 2 にならなかった。
polyfill の入れ方をどうするかが議論された。
Import Maps
ModuleSpecifier fallbacks
code: (js)
セマンティックを定義できる
複数のモジュールでインポートしたときにすべて書き換える必要がある
polyfill がアップデートされたりパッチされたりがハンドリングできない
Import Statement fallbacks
code: (js)
import * from do {
try {
import { CivilDate } from "js:Temporal";
} catch {
}
};
フレキシブル
セキュリティ的に大丈夫?
不十分な実装なのか、polyfill が壊れているのかがわからない
Runtime Hooks
code: (js)
code: (js)
Loader.update("js:Temporal", function(exports) {
if (!exports) {
// Provide missing impletmentation
return { /* My Temporal Implementation */ };
}
// Patch
const { CivilDate } = exports;
CivilDate.prototype.from = function() { /* ... */ };
return { ..exports, CivilDate };
});
かなりフレキシブル
セキュリティ上の問題が起きそう
不十分な実装なのか、polyfill が壊れているのかがわからない
Stage 2 をブロックしている問題として namespace の問題や、ES Modules を使わない Classic Style の場合にどうするかが挙げられている。今後は Built-in Modules という名前でやっていくらしい。
その他
Content Security Policy における Trusted Types について話された。この辺りは Jxck さんの記事が詳しい。
How should we specify Jobs precisely? (slides) Jobs (MicroTask) は実装依存になっており、DOM API と Node.js それぞれで定義されている状態になっている。Job Queue を触る提案として WeakRefs や Atomics.waitAsync がある今、これを ECMASctipt 側に入れようという話。
Status update on non-JS module types (e.g., JSON, CSS, WebIDL) (slides) DOM API としてすでに入っている JSON Modules や WebComponents で提案されている CSS Modules, HTML Modules。 WebAssembly Community Group で提案されている Phase 2 WebAssembly Modules そして Built-in Modules として扱われそうな WebIDL Modules とばらばらになっている状態を一つにしようと議論された。
総括
今回は Dynamic Import や BigInt(Editor Review 待ち)が Stage 4 になり、WeakRefs(Editor Review 待ち)や Optional Chaining, Nullish Coalesing も Stage が上がっていて順調に進んでいるように見える。
また Content Security Policy における Trusted Types で eval を何とかセキュアにするために多くの議題が出たり、色々な Modules の仕様を ECMAScript に入れるための議論がされたりと、DOM API や Node.js はたまた WebAssembly といったレイヤーの高いコミュニティと協力する姿勢が見られたように思う。