Co „kompiluje” kompilator kątowy?

88

Zapytano mnie o to dzisiaj i nie byłem w stanie udzielić właściwej odpowiedzi.

Maszynopis transpiles 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?

Istnieje nawet kompilator „wyprzedzający czas”, który naprawdę wykonuje zauważalną pracę. Za czym tęsknię

Sam JavaScript jest nadal interpretowany, prawda?

codepleb
źródło
6
Zgadzam się z „nie kompilacją”. Zasadniczo jest to kwestia definicji kompilacji . Niektórzy wolą używać transpilacji słów, aby zaznaczyć transformację z TypeScript do JavaScript. Ale tak, w istocie rolą kompilatora Typescript jest po prostu wygenerowanie JavaScript z Typescript.
Pac0
6
@ Pac0 Mogę tu coś nie rozumieć, ale jeśli TypeScript do JavaScript jest transpilacją, czy GCC nie byłoby transpilerem C do kodu maszynowego? Jak zdefiniowałbyś różnicę między transpilerem a kompilatorem?
11684
24
transpilery to kompilatory
user253751
5
Zobacz, co ludzie mają na myśli, kiedy mówią „transpiler”? (oraz uzupełnienie „Moich pierwszych piętnastu kompilatorów” ) (od kogoś, kto pracuje nad kompilatorem), co dowodzi, że „kompilator” to dobre słowo do takich rzeczy.
ShreevatsaR
2
Sam Javascript jest nadal interpretowany, prawda? - już nie, jest kompilowany do kodu maszynowego w locie przez silnik V8
Max Koretskyi

Odpowiedzi:

91

Zakładasz, że kompilacja oznacza pobranie kodu źródłowego i utworzenie kodu maszynowego, kodów niskiego poziomu itp. Jednak kompilowanie w rzeczywistości oznacza po prostu pobranie jednego kodu źródłowego i przekształcenie go w inny. Wydaje się więc rozsądne, aby powiedzieć, że wzięcie Typescript i utworzenie JavaScript jest formą kompilacji. Nie różni się od tego, co (na przykład) robi c #, gdy jest kompilowany do języka IL.

To powiedziawszy, powiedziałbym, że lepszym słowem na to jest Transpiling . Sugerowałbym, że kompilator Typescript jest lepiej opisany jako Transpiler.

Różnica jest subtelna i transpiler może być traktowany jako rodzaj kompilatora; ale (czysty) język skompilowany to (zwykle) zamiana języka wysokiego poziomu na język niższego (wyższego) poziomu (bliżej kodu maszynowego), jak przykład C #. Transpiler przekształca język wysokiego poziomu w język o podobnym poziomie (abstrakcji) (również wysoki poziom). *

Wynik skompilowanego kodu zazwyczaj nie jest językiem, który sam byś napisał . Wynikiem transpilera jest inny język wysokiego poziomu. Teoretycznie możesz napisać IL (jako przykład), ale tak naprawdę jest on zaprojektowany do tworzenia przez kompilator i nie ma żadnych narzędzi ani wsparcia do tego, tworzysz IL, kompilując tylko C # / vb.net. Podczas gdy JavaScript jest użytecznym (i używanym) językiem programowania sam w sobie.

* Wiele zastrzeżeń, ponieważ definicje tych słów i ich użycie są dość niejasne

Liam
źródło
12
Czy JavaScript nie jest na ściśle niższym poziomie niż TypeScript?
Bergi
3
Chociaż wszystko w tej odpowiedzi jest poprawne i pomocne, zwłaszcza że definicja kompilacji jest zawsze kwestią niejasności, nie odpowiada ona na pytanie w tytule. Ta odpowiedź dotyczy tylko języka TypeScript, podczas gdy pytanie dotyczy Angulara. Różnica jest ogromna. Używanie Angulara jest całkowicie możliwe, nawet nie wiedząc, że TS jest czymś. Dziwię się, że ta odpowiedź została zaakceptowana.
Pedro A
3
Kompilator musi zasadniczo zrozumieć cały program, aby wygenerować inny program (który zwykle robi to samo, ale w innym języku - obejmuje to kod maszynowy).
Thorbjørn Ravn Andersen
8
Przeczytałem to dwa razy i nie mogłem znaleźć tutaj odpowiedzi na pytanie. Odpowiedź znajduje się poniżej.
kuncevic.dev
5
Ukryte pytanie, które zadawał OP i dlatego zaakceptowało to, brzmiało: „Czy słuszne jest nazywanie kompilatora Angulara kompilatorem?” - i na to odpowiada ta odpowiedź. Więc +1 ode mnie. Zobacz także Co ludzie mają na myśli, gdy mówią „transpiler”? oraz uzupełnienie „Moje pierwsze piętnaście kompilatorów” .
ShreevatsaR
70

Wydaje się, że zadajesz trzy pytania w jednym:

  • Jaka jest różnica między kompilatorem a transpilerem?
  • Czy Angular i TypeScript implementują kompilatory lub transpilery?
  • Czy istnieje oddzielny kompilator Angular? Co to kompiluje?

Jaka jest różnica między kompilatorem a transpilerem?

@ JörgWMittag udzielił bardzo dobrej odpowiedzi na to pytanie.

Czy Angular i TypeScript implementują kompilatory lub transpilery?

Zarówno TS, jak i Angular implementują prawdziwe kompilatory. Postępują zgodnie z tymi samymi etapami analizy leksykalnej, parsowania, analizy semantycznej i generowania kodu, co kompilatory C / C ++, które tworzą kod asemblerowy (z wyjątkiem prawdopodobnie optymalizacji). Jak widać, klasa / folder nosi nazwę „kompilator” zarówno w języku Angular, jak i TS .

Kompilator kątowy nie jest tak naprawdę powiązany z kompilatorem TypeScript. To są bardzo różne kompilatory.

Czy istnieje oddzielny kompilator Angular? Co to kompiluje?

Angular ma dwa kompilatory:

  • Wyświetl kompilator
  • Kompilator modułów

Zadaniem kompilatora widoku jest przekształcenie szablonu określonego dla szablonu komponentu w wewnętrzną reprezentację komponentu, który jest fabryką widoków, która jest następnie używana do tworzenia instancji widoku .

Oprócz przekształcania szablon, widok kompilator kompiluje także różne informacje metadanych w postaci takich jak dekoratorów @HostBinding, @ViewChildetc.

Załóżmy, że definiujesz komponent i jego szablon w następujący sposób:

@Component({
  selector: 'a-comp',
  template: '<span>A Component</span>'
})
class AComponent {}

Korzystając z tych danych, kompilator generuje następującą, nieco uproszczoną fabrykę komponentów:

function View_AComponent {
  return jit_viewDef1(0,[
      elementDef2(0,null,null,1,'span',...),
      jit_textDef3(null,['My name is ',...])
    ]

Opisuje strukturę widoku komponentu i jest używany podczas tworzenia wystąpienia komponentu. Pierwszy węzeł to definicja elementu, a drugi to definicja tekstu. Możesz zobaczyć, że każdy węzeł otrzymuje potrzebne informacje podczas tworzenia wystąpienia za pomocą listy parametrów. Zadaniem kompilatora jest rozwiązanie wszystkich wymaganych zależności i udostępnienie ich w czasie wykonywania.

Gorąco polecam przeczytanie tych artykułów:

Zobacz także odpowiedź na pytanie Jaka jest różnica między kompilatorem Angular AOT i JIT.

Zadaniem kompilatora modułów jest utworzenie fabryki modułów, która zasadniczo zawiera scalone definicje dostawców.

Aby uzyskać więcej informacji, przeczytaj:

Max Koretskyi
źródło
1
@codepleb, zobacz odpowiedź od Bergi
Max Koretskyi
1
@codepleb Zauważ, że GCC i wiele innych kompilatorów w ogóle nie tworzy kodu maszynowego. W praktyce GCC automatycznie wywołuje systemy w celu wyprodukowania kodu maszynowego, ale kod, który tworzy bez pomocy z zewnątrz, jest jedynie asemblacją, która jest następnie przekazywana zewnętrznemu asemblerowi.
prosfilaes
7
@codepleb Ta odpowiedź jest o wiele lepsza i faktycznie odpowiada na twoje pytanie. Nadal jest czas, aby ponownie rozważyć swój pierwotny osąd.
async
3
@codepleb nie ma dobrego powodu, dla którego termin „transpiler” miałby istnieć lub kiedykolwiek istniał, wszystko to wprowadza w błąd.
Leushenko
2
@stom, przepraszam, to pytanie jest zbyt szerokie. Najbardziej pozytywna odpowiedź jest jednak całkiem dobra
Max Koretskyi
54

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 .

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.

Jörg W Mittag
źródło
4
+1 za dokładne wyjaśnienie pojęcia kompilacji, a jeśli mógłbym, jeszcze jedno +1 za te wypunktowane punkty. Bardzo pomocny.
Pedro A
1
Chociaż, muszę powiedzieć, technicznie to nie odpowiada na pytanie w tytule ... Jednak nadal zasłużone +1 ode mnie!
Pedro A
Zgadzam się, że to dorozumiane, ale odpowiedź na pytanie w tytule brzmi: „wszystko, co wyszczególnia PO, ponieważ nie jest kompilacją, jest tym, czym jest kompilacja kątowa”.
Jörg W Mittag
Naprawdę dobre wyjaśnienie, jak to właściwie dotyczy konwencji nazewnictwa, a nie merytorycznej różnicy. Być może można by to poprawić, wspominając o mikrokodzie - aby zwrócić uwagę, że nawet na poziomie kodu maszynowego nie jesteś
``
1
Jakoś pamiętam, jak się nauczyłem, czym jest kompilator. Gdyby ktoś powiedział mi wtedy, że „kompilator” jest synonimem „tłumacza kodu”, byłoby o wiele łatwiej znaleźć to, do czego służy i dlaczego go potrzebujemy. Jasne, z dzisiejszego punktu widzenia brzmi to absurdalnie, ale to po prostu mówi mi jeszcze raz, jak wiele można zyskać mając odpowiednią osobę, która może go czegoś nauczyć. Dziękuję Ci. :)
codepleb
18

Uzyskanie napisanego kodu do uruchomienia w przeglądarce obejmuje dwie rzeczy:

1) Transpiling maszynopisu do JavaScript . To jest rodzaj rozwiązanego problemu. Myślę, że po prostu używają webpacka.

2) Kompilowanie abstrakcji kątowych w JavaScript . Mam na myśli takie rzeczy jak komponenty, rury, dyrektywy, szablony itp. To jest to, nad czym pracuje angular core team.

Jeśli naprawdę jesteś zainteresowany tym drugim bitem, kompilatorem kątowym, zobacz, jak autor kompilatora Tobias Bosch wyjaśnia kompilator Angular na AngularConnect 2016 .

Myślę, że między transpilacją a kompilacją dzieje się trochę zamieszania. To w pewnym sensie nie ma znaczenia i jest kwestią osobistego gustu, oba są po prostu przekształcane między reprezentacjami kodu. Ale definicja, której osobiście używam, jest taka, że transpilacja odbywa się między dwoma różnymi językami na podobnym poziomie abstrakcji (np. Maszynopis do javascript), podczas gdy kompilacja wymaga obniżenia poziomu abstrakcji. Myślę, że od szablonów, komponentów, potoków, dyrektyw itp. Do javascript jest krokiem w dół drabiny abstrakcji i dlatego nazywa się to kompilatorem.

Nathan Cooper
źródło
1

Kompilator kątowy

Jedną z najważniejszych zmian z Angular 4 do 5 jest to, że kompilator został przepisany, aby był szybszy i dokładniejszy. W przeszłości aplikacje Angular korzystały z tego, co nazywamy kompilacją Just-in-Time (JIT), w której aplikacja była kompilowana w czasie wykonywania w przeglądarce przed uruchomieniem. Aktualizacje kompilatora w Angular 5 przyspieszyły przejście do AOT, co sprawiło, że aplikacja działała szybciej, ponieważ wykonuje mniej kompilacji podczas uruchamiania aplikacji. AOT stają się domyślnie włączone w każdej kompilacji produkcyjnej od wersji 1.5 interfejsu wiersza polecenia Angular.

Powiedzmy, że chcemy zbudować aplikację do wdrożenia i uruchomić następujące polecenie:

ng build --prod

Dzieje się kilka rzeczy: wersja produkcyjna, minifikacja, zestawy zasobów, haszowanie nazw plików, potrząsanie drzewem, AOT ... (możemy to włączyć / wyłączyć za pomocą flag, np. Aot = false). Krótko mówiąc, flaga prod tworzy zoptymalizowany pakiet aplikacji, wykonując kompilację AOT przy użyciu ngc (kompilatora Angular), aby utworzyć zoptymalizowany kod gotowy dla przeglądarki ( tak, wstępnie kompiluje szablony ).

Kompilator języka TypeScript

Kompilator TypeScript, tsc , jest odpowiedzialny za kompilowanie plików TypeScript. To kompilator jest odpowiedzialny za implementację funkcji TypeScript, takich jak typy statyczne, a wynikiem jest czysty JavaScript, z którego zostały usunięte słowa kluczowe i wyrażenia TypeScript.

Kompilator TypeScript ma dwie główne cechy: jest to transpiler i narzędzie do sprawdzania typów. Kompilator transpiluje TypeScript do JavaScript. Wykonuje następujące transformacje kodu źródłowego:

  • Usuń wszystkie adnotacje typu.
  • Skompiluj nowe funkcje JavaScript dla starszych wersji JavaScript.
  • Skompiluj funkcje TypeScript, które nie są standardowym JavaScriptem.

Wywołując go, kompilator wyszukuje konfiguracje załadowane do tsconfig.json (szczegółową listę wszystkich opcji kompilatora wraz z wartościami domyślnymi można znaleźć tutaj ).

Pod wieloma względami kompilator TypeScript działa jak każdy inny kompilator. Ale jest jedna różnica, która może wyłapać nieostrożnych: domyślnie kompilator nadal emituje kod JavaScript, nawet jeśli napotka błąd. Na szczęście to zachowanie można wyłączyć, ustawiając ustawienie noEmitOnErrorkonfiguracyjne na true w pliku tsconfig.json.

Uwaga : tsc i ngc mają różne cele i nie chodzi o wybieranie jednego z nich. Ta odpowiedź może być interesująca .

Ta odpowiedź została stworzona na podstawie treści z następujących książek

  • Cloe, M. (2018). „Projekty Angular 5: Naucz się budować jednostronicowe aplikacje internetowe przy użyciu ponad 70 projektów”.

  • Dewey, B., Grossnicklaus, K., Japikse, P. (2017). „Tworzenie aplikacji internetowych w programie Visual Studio 2017: korzystanie z .NET Core i nowoczesnych struktur JavaScript”.

  • Freeman, A. (2019). „Essential TypeScript: od początkującego do profesjonalnego”.

  • Ghiya, P. (2018). „Mikrousługi TypeScript”.

  • Iskandar, A., Chivukulu, S. (2019). „Tworzenie stron internetowych za pomocą Angular i Bootstrap - wydanie trzecie”.

  • Hennessy, K., Arora, C. (2018). „Angular 6 by Example”.

  • Jansen, R., Wolf, I., Vane, V. (2016). „TypeScript: Modern JavaScript Development”.

  • Mohammed, Z. (2019). „Angular Projects”.

  • Seshadri, S. (2018). „Angular: działa i działa”.

  • Wilken, J. (2018). „Angular w akcji”.

Tiago Martins Peres 李大仁
źródło