🎄

ENCA 19日目: `eval?.()` の direct eval 化(リジェクト)

2024/12/19に公開

direct eval とは

eval 函数を実行する際、参照される変数名が eval そのままで実行される場合を direct eval と呼び、そうでない indirect eval と区別しています。direct eval はそのスコープでの実行となりますが indirect eval は別のコンテキストで実行されます(新しく <script> タグが作られて実行されると考えるとわかりやすいかもしれません)。

globalThis.foo = "global foo";

{
  const foo = "shadowed foo";

  // direct eval
  console.log(eval("foo")); // "shadowed foo"

  // indirect eval
  const indirectEval = eval;
  console.log(indirectEval("foo")); // "global foo"
}

なぜこんなややこしい仕様になっているかというとガベージコレクションのためです。direct eval の場合は解析時に eval 函数が使われることがわかりますが indirect eval は検知できません。スコープ内の変数にアクセスできてしまうとガベージコレクションが出来なくなってしまいます。

JavaScript エンジンのガベージコレクションについては以下の記事が詳しいです。

https://jakearchibald.com/2024/garbage-collection-and-closures/

eval?.() の direct eval 化(リジェクト)

ES2020 に Optional Chaining が入りました。さて eval?.() のように実行した場合、解析時に eval が使われていることを検知することが出来ますが indirect eval として扱われていました。2020年7月にこれを direct eval に変更、もしくはいっそのこと SyntaxError としてしまうかが議論されましたが、結果としてはいずれの変更も受け入れられずそのままとなりました。

https://github.com/tc39/ecma262/pull/2063

Discussion