Meccanismi interni del compilatore

Le decisioni progettuali chiave che rendono Perry veloce

01NaN-Boxing

I valori vengono memorizzati come float a 64 bit con pattern di bit speciali per i puntatori, permettendo i tipi union senza overhead runtime. Questo consente a (string | number)[] di funzionare efficientemente.

Codificando le informazioni di tipo direttamente nei bit di payload NaN IEEE 754, Perry evita la necessità di tagged union o valori incapsulati. Una singola parola a 64 bit può rappresentare qualsiasi tipo di valore JavaScript — i numeri usano la loro rappresentazione naturale, mentre le stringhe, gli oggetti e altri valori allocati nell'heap usano pattern di bit puntatore che ricadono nell'intervallo NaN.

02Monomorphization

I generici vengono specializzati a tempo di compilazione, come in Rust. Ogni istanziazione di tipo genera codice ottimizzato, eliminando l'overhead del controllo dei tipi a runtime.

Quando scrivi Array'<'number'>' e Array'<'string'>', Perry genera due implementazioni separate e completamente ottimizzate. Questo significa che le operazioni sugli array di numeri usano aritmetica macchina diretta — nessun type guard, nessun dispatch dinamico, nessun boxing.

03Static Dispatch

Nessuna tabella virtuale. Le chiamate ai metodi vengono risolte a tempo di compilazione, abilitando chiamate di funzione dirette e ottimizzazioni di inlining.

I runtime OOP tradizionali usano vtable per la risoluzione dei metodi, aggiungendo un livello di indirezione ad ogni chiamata. Perry risolve tutte le chiamate ai metodi staticamente durante la compilazione, trasformando le chiamate ai metodi di interfaccia in salti diretti. Questo sblocca anche un inlining aggressivo — i metodi piccoli vengono spesso integrati direttamente nei loro siti di chiamata.

04Zero-Cost Abstractions

Le classi, le interfacce e i generici TypeScript vengono compilati in codice nativo efficiente senza overhead di rappresentazione runtime.

Il sistema di tipi di TypeScript esiste solo a tempo di compilazione — e Perry lo prende alla lettera. Le interfacce non producono codice runtime. Le gerarchie di classi vengono appiattite. I vincoli generici vengono risolti in tipi concreti. Il binario compilato contiene solo la logica effettiva, senza il meccanismo di astrazione che gli interpreti tipicamente portano con sé.