Maszynopis przenosi się do JS. Następnie jest trzęsienie drzewa, „mniej” (opcjonalnie) i co jeszcze w procesie wdrażania. Ale nic takiego (afaik) nie ma nic wspólnego z „kompilacją”. Wszystko jest pakowane i mocno optymalizowane, ale tak naprawdę nie jest kompilowane, prawda?
Kompilacja oznacza przekształcenie programu napisanego w języku A w semantycznie równoważny program napisany w języku B taki, że ocena skompilowanego programu zgodnie z regułami języka B (na przykład interpretacja za pomocą interpretera dla B ) daje ten sam wynik i ma takie same skutki uboczne jak ocena oryginalnego programu zgodnie z regułami języka A (np. interpretacja z tłumaczem języka A ).
Kompilacja oznacza po prostu tłumaczenia program z języka A na język B . To wszystko, co to znaczy. (Należy również pamiętać, że jest całkowicie możliwe, że A i B są tym samym językiem).
W niektórych przypadkach mamy bardziej wyspecjalizowane nazwy dla niektórych typów kompilatorów, w zależności od tego, czym są A i B oraz co robi kompilator:
- jeśli A jest postrzegany jako język asemblera, a B jest postrzegany jako język maszynowy, wtedy nazywamy go asemblerem ,
- jeśli A jest postrzegane jako język maszynowy, a B jest postrzegane jako język asemblera, wtedy nazywamy to deasemblerem ,
- jeśli A jest postrzegane jako niższy poziom niż B , wtedy nazywamy to dekompilatorem ,
- jeśli A i B są tym samym językiem, a wynikowy program jest w jakiś sposób szybszy lub lżejszy, wówczas nazywamy go optymalizatorem ,
- jeśli A i B są tymi samymi językami, a wynikowy program jest mniejszy, wówczas nazywamy go minifikatorem ,
- jeśli A i B są tymi samymi językami, a wynikowy program jest mniej czytelny, wówczas nazywamy go obfuskatorem ,
- jeśli A i B są postrzegane jako mniej więcej na tym samym poziomie abstrakcji, wówczas nazywamy to transpilerem i
- jeśli A i B są postrzegane jako mniej więcej na tym samym poziomie abstrakcji, a wynikowy program zachowuje formatowanie, komentarze i intencje programisty tak, że możliwe jest utrzymanie wynikowego programu w taki sam sposób, jak program oryginalny, wówczas wywołujemy to narzędzie do przeprojektowania .
Zwróć także uwagę, że starsze źródła mogą używać terminów „tłumaczenie” i „tłumacz” zamiast „kompilacja” i „kompilator”. Na przykład C mówi o „jednostkach tłumaczeniowych”.
Możesz również natknąć się na termin „procesor języka”. Może to oznaczać kompilator, interpreter lub kompilatory i interpretery w zależności od definicji.
Sam JavaScript jest nadal interpretowany, prawda?
JavaScript to język. Języki to zbiór logicznych reguł i ograniczeń. Języki nie są interpretowane ani kompilowane. Języki po prostu są .
Kompilacja i interpretacja to cechy kompilatora lub interpretera (duh!). Każdy język można zaimplementować za pomocą kompilatora, a każdy język za pomocą interpretera. Wiele języków ma zarówno kompilatory, jak i tłumaczy. Wiele nowoczesnych wysokowydajnych silników wykonawczych ma co najmniej jeden kompilator i co najmniej jeden interpreter.
Te dwa terminy należą do różnych warstw abstrakcji. Gdyby angielski był językiem maszynowym, „interpreted-language” byłby błędem typu.
Zauważ również, że niektóre języki nie mają ani interpretera, ani kompilatora. Są języki, które nie mają żadnej implementacji. Mimo to są to języki i można w nich pisać programy. Po prostu nie możesz ich uruchomić.
Należy również pamiętać, że wszystko jest interpretowane w pewnym momencie : jeśli chcesz wykonać coś, to należy go interpretować. Kompilacja po prostu tłumaczy kod z jednego języka na inny. Nie działa. Prowadzi to interpretacja . (Czasami, gdy interpreter jest zaimplementowany sprzętowo, nazywamy go „procesorem”, ale nadal jest to interpreter).
Przykład: każda obecnie istniejąca implementacja głównego nurtu JavaScript ma kompilator.
V8 zaczynał jako czysty kompilator: kompilował JavaScript bezpośrednio do średnio zoptymalizowanego natywnego kodu maszynowego. Później dodano drugi kompilator. Teraz są dwa kompilatory: lekki kompilator, który tworzy umiarkowanie zoptymalizowany kod, ale sam kompilator jest bardzo szybki i zużywa mało pamięci RAM. Ten kompilator wstrzykuje również kod profilujący do skompilowanego kodu. Drugi kompilator to cięższy, wolniejszy i droższy kompilator, który jednak generuje znacznie ciaśniejszy, znacznie szybszy kod. Wykorzystuje również wyniki kodu profilującego wstrzykniętego przez pierwszy kompilator do podejmowania dynamicznych decyzji optymalizacyjnych. Ponadto decyzja, który kod należy ponownie skompilować za pomocą drugiego kompilatora, jest podejmowana na podstawie tych informacji o profilowaniu. Zwróć uwagę, że w żadnym momencie nie jest zaangażowany tłumacz. V8 nigdy nie interpretuje, zawsze się kompiluje. To nie robi nie zawierają nawet tłumacza. (Właściwie wydaje mi się, że obecnie tak jest, opisuję pierwsze dwie iteracje.)
SpiderMonkey kompiluje JavaScript do kodu bajtowego SpiderMonkey, który następnie interpretuje. Interpreter profiluje również kod, a następnie kod, który jest wykonywany najczęściej, jest kompilowany przez kompilator do natywnego kodu maszynowego. Tak więc SpiderMonkey zawiera dwa kompilatory: jeden z kodu bajtowego JavaScript do kodu bajtowego SpiderMonkey, a drugi od kodu bajtowego SpiderMonkey do natywnego kodu maszynowego.
Prawie wszystkie silniki wykonawcze JavaScript (z wyjątkiem V8) są zgodne z tym modelem kompilatora AOT, który kompiluje JavaScript do kodu bajtowego, oraz silnika trybu mieszanego, który przełącza się między interpretacją i kompilacją tego kodu bajtowego.
Napisałeś w komentarzu:
Naprawdę myślałem, że kod maszynowy jest gdzieś zaangażowany.
Co w ogóle oznacza „kod maszynowy”?
Jaki język maszynowy jednego człowieka jest językiem pośrednim drugiego człowieka i odwrotnie? Na przykład istnieją procesory, które mogą natywnie wykonywać kod bajtowy JVM, na takim procesorze kod bajtowy JVM jest rodzimym kodem maszynowym. Są też interpretery dla kodu maszynowego x86, kiedy ten kod maszynowy x86 jest interpretowany jako kod bajtowy.
Istnieje interpreter x86 o nazwie JPC napisany w Javie. Jeśli uruchomię kod maszynowy x86 na JPC działającym na natywnym procesorze JVM… który jest kodem bajtowym, a który natywnym? Jeśli skompiluję kod maszynowy x86 do JavaScript (tak, są narzędzia, które mogą to zrobić) i uruchomię go w przeglądarce na moim telefonie (który ma procesor ARM), który jest kodem bajtowym, a który natywnym kodem maszynowym? Co się stanie, jeśli kompilowany przeze mnie program jest emulatorem SPARC i używam go do uruchamiania kodu SPARC?
Zauważ, że każdy język wywołuje abstrakcyjną maszynę i jest językiem maszynowym dla tej maszyny. Tak więc każdy język (w tym języki bardzo wysokiego poziomu) jest rodzimym kodem maszynowym. Możesz również napisać tłumacza na każdy język. Tak więc każdy język (w tym kod maszynowy x86) nie jest rodzimy.