Il dattiloscritto trapela a JS. Poi c'è lo scuotimento degli alberi, "less" (opzionale) e cos'altro nel processo di implementazione. Ma niente del genere (afaik) ha a che fare con la "compilazione". Tutto viene raggruppato e fortemente ottimizzato, ma non è effettivamente compilato, giusto?
Compilazione significa trasformare un programma scritto in una lingua A in un programma semanticamente equivalente scritto in una lingua B tale che valutare il programma compilato secondo le regole del linguaggio B (ad esempio interpretarlo con un interprete per B ) produce lo stesso risultato e ha il stessi effetti collaterali della valutazione del programma originale secondo le regole del linguaggio A (ad esempio interpretarlo con un interprete per A ).
Compilazione significa semplicemente traducendo un programma dal linguaggio A alla lingua B . È tutto ciò che significa. (Notare inoltre che è perfettamente possibile che A e B siano la stessa lingua.)
In alcuni casi, abbiamo nomi più specializzati per certi tipi di compilatori, a seconda di cosa sono A e B e cosa fa il compilatore:
- se A è percepito come linguaggio assembly e B è percepito come linguaggio macchina, allora lo chiamiamo assemblatore ,
- se A è percepito come linguaggio macchina e B è percepito come linguaggio assembly, allora lo chiamiamo disassemblatore ,
- se A viene percepito come di livello inferiore a B , lo chiamiamo decompilatore ,
- se A e B sono la stessa lingua e il programma risultante è in qualche modo più veloce o più leggero, lo chiamiamo ottimizzatore ,
- se A e B sono le stesse lingue e il programma risultante è più piccolo, lo chiamiamo minificatore ,
- se A e B sono le stesse lingue e il programma risultante è meno leggibile, lo chiamiamo offuscatore ,
- se A e B sono percepiti come all'incirca allo stesso livello di astrazione, allora lo chiamiamo un transpiler , e
- se A e B sono percepiti all'incirca allo stesso livello di astrazione e il programma risultante conserva la formattazione, i commenti e l'intento del programmatore in modo tale che sia possibile mantenere il programma risultante allo stesso modo del programma originale, allora chiamiamo è uno strumento di reingegnerizzazione .
Inoltre, nota che le fonti più vecchie possono usare i termini "traduzione" e "traduttore" invece di "compilazione" e "compilatore". Ad esempio, C parla di "unità di traduzione".
Potresti anche imbatterti nel termine "elaboratore di linguaggio". Questo può significare un compilatore, un interprete o entrambi compilatori e interpreti a seconda della definizione.
Javascript stesso è ancora interpretato, giusto?
JavaScript è un linguaggio. Le lingue sono un insieme di regole e restrizioni logiche. Le lingue non vengono interpretate o compilate. Le lingue lo sono .
La compilazione e l'interpretazione sono tratti di un compilatore o interprete (duh!). Ogni lingua può essere implementata con un compilatore e ogni lingua può essere implementata con un interprete. Molte lingue hanno sia compilatori che interpreti. Molti motori di esecuzione moderni ad alte prestazioni hanno almeno un compilatore e almeno un interprete.
Questi due termini appartengono a diversi livelli di astrazione. Se l'inglese fosse una lingua digitata, "interpreted-language" sarebbe un errore di tipo.
Nota anche che alcune lingue non hanno né un interprete né un compilatore. Ci sono lingue che non hanno alcuna implementazione. Tuttavia, sono lingue e puoi scrivere programmi in esse. Non puoi semplicemente eseguirli.
Inoltre, nota che tutto viene interpretato a un certo punto : se vuoi eseguire qualcosa, devi interpretarlo. La compilazione traduce semplicemente il codice da una lingua all'altra. Non lo esegue. L'interpretazione lo fa funzionare. (A volte, quando un interprete è implementato nell'hardware, lo chiamiamo "CPU", ma è comunque un interprete.)
Caso in questione: ogni singola implementazione JavaScript mainstream attualmente esistente ha un compilatore.
V8 è iniziato come un compilatore puro: ha compilato JavaScript direttamente in codice macchina nativo moderatamente ottimizzato. Successivamente è stato aggiunto un secondo compilatore. Ora, ci sono due compilatori: un compilatore leggero che produce codice moderatamente ottimizzato ma il compilatore stesso è molto veloce e usa poca RAM. Questo compilatore inietta anche il codice di profilatura nel codice compilato. Il secondo compilatore è un compilatore più pesante, più lento e più costoso, che, tuttavia, produce codice molto più stretto e molto più veloce. Utilizza anche i risultati del codice di profilazione iniettato dal primo compilatore per prendere decisioni di ottimizzazione dinamica. Inoltre, la decisione su quale codice ricompilare utilizzando il secondo compilatore viene presa in base a tali informazioni di profilo. Notare che in nessun momento è coinvolto un interprete. V8 non interpreta mai, compila sempre. Non lo fa Non contengono nemmeno un interprete. (In realtà, credo di sì, sto descrivendo le prime due iterazioni.)
SpiderMonkey compila JavaScript nel bytecode SpiderMonkey, che poi interpreta. L'interprete profila anche il codice, quindi il codice che viene eseguito più spesso viene compilato da un compilatore in codice macchina nativo. Quindi, SpiderMonkey contiene due compilatori: uno da JavaScript a SpiderMonkey bytecode e un altro da SpiderMonkey a codice macchina nativo.
Quasi tutti i motori di esecuzione JavaScript (ad eccezione del V8) seguono questo modello di un compilatore AOT che compila JavaScript in bytecode e un motore a modalità mista che passa dall'interpretazione alla compilazione di quel bytecode.
Hai scritto in un commento:
Stavo davvero pensando che il codice macchina fosse coinvolto da qualche parte.
Cosa significa anche "codice macchina"?
Qual è il linguaggio macchina di un uomo è il linguaggio intermedio di un altro uomo e viceversa? Ad esempio, ci sono CPU che possono eseguire nativamente bytecode JVM, su una tale CPU, bytecode JVM è codice macchina nativo. E ci sono interpreti per il codice macchina x86, quando si esegue di quei codici macchina x86 viene interpretato bytecode.
C'è un interprete x86 chiamato JPC scritto in Java. Se eseguo il codice macchina x86 su JPC in esecuzione su una CPU JVM nativa ... qual è il bytecode e quale è il codice nativo? Se compilo il codice macchina x86 in JavaScript (sì, ci sono strumenti che possono farlo) e lo eseguo in un browser sul mio telefono (che ha una CPU ARM), qual è il bytecode e qual è il codice macchina nativo? E se il programma che sto compilando è un emulatore SPARC e lo utilizzo per eseguire codice SPARC?
Nota che ogni linguaggio induce una macchina astratta, ed è linguaggio macchina per quella macchina. Quindi, ogni lingua (compresi i linguaggi di altissimo livello) è un codice macchina nativo. Inoltre, puoi scrivere un interprete per ogni lingua. Quindi, ogni lingua (incluso il codice macchina x86) non è nativa.