コンパイラ内部構造
Perryを高速にする主要な設計上の決定
01NaN-Boxing
値はポインタ用の特殊なビットパターンを持つ64ビット浮動小数点数として格納され、ランタイムオーバーヘッドなしでユニオン型を実現します。これにより(string | number)[]が効率的に動作します。
IEEE 754のNaNペイロードビットに型情報を直接エンコードすることで、Perryはタグ付きユニオンやボックス化された値の必要性を回避します。1つの64ビットワードがあらゆるJavaScript値型を表現できます——数値は自然な表現を使用し、文字列、オブジェクト、その他のヒープ割り当て値はNaN範囲内に収まるポインタビットパターンを使用します。
02Monomorphization
ジェネリクスはRustと同様にコンパイル時に特殊化されます。各型のインスタンス化は最適化されたコードを生成し、ランタイムの型チェックオーバーヘッドを排除します。
Array'<'number'>'とArray'<'string'>'を書くと、Perryは2つの別々の完全に最適化された実装を生成します。つまり、数値の配列操作は直接的なマシン算術を使用します——タイプガードなし、動的ディスパッチなし、ボクシングなし。
03Static Dispatch
仮想テーブルなし。メソッド呼び出しはコンパイル時に解決され、直接的な関数呼び出しとインライン化の最適化を可能にします。
従来のOOPランタイムはメソッド解決にvtableを使用し、すべての呼び出しに間接参照のレイヤーを追加します。Perryはコンパイル時にすべてのメソッド呼び出しを静的に解決し、インターフェースメソッド呼び出しを直接ジャンプに変換します。これにより積極的なインライン化も可能になります——小さなメソッドは呼び出しサイトに直接埋め込まれることが多いです。
04Zero-Cost Abstractions
TypeScriptのクラス、インターフェース、ジェネリクスは、ランタイム表現のオーバーヘッドなしで効率的なネイティブコードにコンパイルされます。
TypeScriptの型システムはコンパイル時にのみ存在し、Perryはこれを文字通りに実装します。インターフェースはランタイムコードを一切生成しません。クラス階層はフラット化されます。ジェネリック制約は具体的な型に解決されます。コンパイルされたバイナリには実際のロジックのみが含まれ、インタプリタが通常持つ抽象化の仕組みは含まれません。