O JavaScript é interpretado por design?

73

Sou cauteloso ao fazer esta pergunta porque ela pode parecer excessivamente exigente. Acabei de abrir o JavaScript: The Definitive Guide, e ele afirma na primeira página do capítulo 1

"JavaScript é uma linguagem de programação interpretada de alto nível, dinâmica e sem tipo"

Então, devo considerar que a parte interpretada é um requisito na especificação da linguagem ou é enganoso dizer que a linguagem é uma linguagem de programação interpretada quando se respeita a diferença entre uma linguagem e suas muitas implementações?

Aparentemente, não há compiladores estáticos para JavaScript - https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript , talvez seja apenas um reflexo disso.

Matt Esch
fonte
Por um tempo, houve um jscript.net semelhante ao AS3 / ES4 "perdido". Foi compilado pelo bytecode para o CIL.
Ei
13
A V8 afirma explicitamente não ser um intérprete, mas um compilador.
Pimvdb
@GGG O JScript.Net ainda está vivo e ... doentio. Mas ainda vivo. msdn.microsoft.com/pt-br/library/72ryt15a.aspx
Jetti 6/12
11
FWIW, o bit "sem tipo" não é rigorosamente verdade quer
Rob Agar
O Firefox havia acabado de lançar o primeiro compilador JIT baseado em navegador no ano em que a pergunta foi respondida no FF 3.5, por isso provavelmente não era amplamente conhecido na época. Acredito que os JITs modernos realmente fazem muita compilação (ou pelo menos se preparam para compilar) na primeira passagem de um documento JS para fazer coisas como identificar e armazenar métodos em cache isolados em um determinado escopo.
Erik Reppen

Respostas:

50

Então, devo considerar que a parte interpretada é um requisito na especificação da linguagem ou é enganoso dizer que a linguagem é uma linguagem de programação interpretada quando se respeita a diferença entre uma linguagem e suas muitas implementações?

Os geeks da linguagem EcmaScript costumam usar o termo "intérprete de ES" para se referir a uma implementação do EcmaScript, mas a especificação não usa esse termo. A visão geral do idioma, em particular, descreve o idioma em termos agnósticos de intérpretes:

O ECMAScript é baseado em objetos: recursos básicos de host e linguagem são fornecidos por objetos, e um programa ECMAScript é um cluster de objetos em comunicação.

Portanto, o EcmaScript assume um "ambiente host" que é definido como um provedor de definições de objetos, incluindo todos aqueles que permitem E / S ou quaisquer outros links para o mundo externo, mas não exigem um intérprete.

A semântica de instruções e expressões na linguagem é definida em termos de especificação de conclusão que são implementadas trivialmente em um intérprete, mas a especificação não exige isso.

8.9 O tipo de especificação de conclusão

O tipo de conclusão é usada para explicar o comportamento de declarações ( break, continue, returne throw) que efectue transferências não-locais de controle. Os valores do tipo Conclusão são triplos no formato ( tipo , valor , destino ), em que tipo é normal , valor de interrupção , continuação , retorno ou lançamento , valor é qualquer valor do idioma ECMAScript ou vazio , e destino é qualquer identificador ECMAScript ou vazio .

O termo "conclusão abrupta" refere-se a qualquer conclusão com um tipo diferente do normal .

As transferências de controle não locais podem ser convertidas em matrizes de instruções com saltos, permitindo a compilação nativa ou de código de bytes.

"Mecanismo EcmaScript" pode ser a melhor maneira de expressar a mesma idéia.


Aparentemente, não há compiladores estáticos para JavaScript

Isso não é verdade. O "intérprete" da V8 compila internamente o código nativo, o Rhino opcionalmente compila internamente o bytecode Java e vários intérpretes Mozilla ({Trace, Spider, Jager} Monkey) usam um compilador JIT.

V8 :

A V8 aumenta o desempenho compilando o JavaScript no código da máquina nativo antes de executá-lo, em vez de executar o bytecode ou interpretá-lo.

Rinoceronte :

public final void setOptimizationLevel(int optimizationLevel)

Defina o nível de otimização atual. Espera-se que o nível de otimização seja um número inteiro entre -1 e 9. Quaisquer valores negativos serão interpretados como -1 e quaisquer valores maiores que 9 serão interpretados como 9. Um nível de otimização de -1 indica que o modo interpretativo será sempre usava. Os níveis de 0 a 9 indicam que os arquivos de classe podem ser gerados. Níveis mais altos de otimização compensam o desempenho do tempo de compilação pelo desempenho do tempo de execução. O nível do otimizador não pode ser definido como maior que -1 se o pacote do otimizador não existir no tempo de execução.

TraceMonkey :

O TraceMonkey adiciona compilação de código nativo ao mecanismo JavaScript® da Mozilla (conhecido como “SpiderMonkey”). Baseia-se em uma técnica desenvolvida na UC Irvine chamada “trace trees” e com base em código e idéias compartilhadas com o projeto Tamarin Tracing. O resultado líquido é um enorme aumento de velocidade, tanto no cromo do navegador quanto no conteúdo da página da Web.

Mike Samuel
fonte
11
Obrigado por esta resposta, ele realmente responde à pergunta. Suponho que o comentário final sobre nenhuma compilação estática foi o que causou o tumulto sobre quais implementações realmente compilam código e quais não. Tudo o que me interessava era a validade da declaração "JavaScript é uma linguagem interpretada" que, dadas as citações de implementação e a falta de definição das especificações, parece ser falsa. Não é encorajador para o segundo parágrafo de um "Guia Definitivo", mas acho que vou continuar com ele.
Matt Esch
@ me232, a afirmação era substancialmente verdadeira antes de 2008. Rhino antecede esse intérprete, mas não era um grande intérprete e, portanto, poucos teriam criticado o "Guia Definitivo" na época por ignorá-lo. Como não li o livro, não posso comentar como essa frase é representativa de sua qualidade geral.
Mike Samuel
Qual é a definição de "compilador estático". Eu pensei que essa definição significava que a compilação acontece apenas uma vez e você recebe um balde estático (ou seja, imutável) de bits que você executa. AFAIK, não é assim que funciona qualquer mecanismo JavaScript. É por isso que eles têm de-optimizationetapas. Em outras palavras, o JavaScript é compilado por esses mecanismos, mas não é estaticamente compilado.
precisa
@ gman, o gerador de bytecodes do Rhino funciona dessa maneira.
Mike Samuel
AFAIK não é esse o caso. O Rhino pode incluir outros arquivos JavaScript que precisam ser compilados em tempo de execução. Isso não é complicação estática .
gman
20

A VM JavaScript V8 usada no Chrome não inclui um intérprete. Em vez disso, consiste em dois compiladores e compila o código rapidamente. Um dos compiladores é executado rapidamente, mas gera código ineficiente, o outro é um compilador de otimização.

Eu posso entender por que algumas pessoas considerariam essa "trapaça", pois a V8 recebe o código-fonte como entrada toda vez que o código é executado e o usuário precisa ter o V8 instalado. Mas considere um compilador que emite um executável que inclua um intérprete e um bytecode completos. Então você teria um programa independente. Simplesmente não seria muito eficiente.

Jørgen Fogh
fonte
19

O surgimento de compiladores JIT para linguagens de script obscureceu a linha entre compilação e interpretação a um ponto em que a pergunta não significa muito. É apenas interpretação quando o mecanismo lê uma linha de código e a executa imediatamente? (Geralmente, os scripts de shell ainda são implementados dessa maneira.) É interpretação quando o mecanismo pega o arquivo inteiro, o compila imediatamente em algum código de byte e depois interpreta o código de byte? (O mecanismo Mozilla de primeiro estágio funciona dessa maneira, assim como o CPython.) É interpretação quando o mecanismo analisa uma função de cada vez e o JIT a compila no código nativo? E os mecanismos que compilam o arquivo inteiro em código de bytes e, em seguida, JIT uma função por vez, conforme necessário? Atualmente, a maioria dos mecanismos de script funciona dessa maneira,

Existem muitos tons entre compilação e interpretação.

Eu acho que a definição mais útil para interpretação é "é alimentado o código fonte do programa no tempo de execução, sem uma etapa antecipada separada". Por essa definição, todos os mecanismos JavaScript são intérpretes. Mas essa certamente não é a única definição possível de interpretação.

Mas o JavaScript foi projetado para interpretação? De certa forma, sim: ele possui uma evalfunção e o Functionconstrutor que você pode fornecer ao código do programa como uma string que será executada. A capacidade de construir dinamicamente o código do programa em tempo de execução requer que o mecanismo seja capaz de interpretar o código-fonte. Mas isso não significa que você não pode fazer todo o resto antes do tempo. Mesmo em uma linguagem compilada como C ++ e C #, você pode pegar o código-fonte, compilá-lo na memória para um novo código de máquina e depois executá-lo. Existem até bibliotecas para isso: LLVM + Clang em C ++ e o projeto Roslyn em C #.

Além disso, o mecanismo de entrega para JavaScript é o código-fonte; não existe uma forma de código de bytes reconhecido. C # e Java têm seu código de bytes oficial e todos esperam que o C ++ seja entregue como código de máquina. Mas esse ainda não é um aspecto inerente à linguagem, apenas um cenário de uso dominante. De fato, o ActionScript relativamente próximo do JavaScript no Flash é realmente entregue como código de bytes (o compilador do Flash pré-compila todos os scripts).

Sebastian Redl
fonte
4

Não existe uma definição totalmente acordada de 'interpretado' versus 'compilado'. Na distinção clássica, as linguagens compiladas produzem um executável binário independente, enquanto as linguagens interpretadas requerem um tempo de execução implantado para executar o código. Máquinas virtuais, bytecode e assim por diante desfocam a distinção.

Mas aqui está uma definição possivelmente útil: Uma linguagem interpretada é uma linguagem em que o tempo de execução do idioma padrão é capaz de pegar o texto do código-fonte como entrada e executá-lo. Por essa definição, scripts Perl, Python, Ruby, JavaScript e shell e similares são interpretados (mesmo que usem etapas intermediárias como bytecode ou mesmo código nativo). Java, C #, C etc. não são. E o JavaScript é interpretado por definição, mesmo que a especificação não use a palavra exata.

JacquesB
fonte
Hmm, eu não gosto de colocar Java e C na mesma categoria. Talvez uma distinção melhor seja a linguagem mais comumente distribuída como (A) código-fonte, (B) código intermediário ou (C) código de máquina. Por exemplo, A = javascript, B = Java, C = C.
precisa
Chamar um idioma interpretado ou compilado não é correto. Por exemplo, sob essa regra, você concorda que C ++ é uma linguagem compilada, certo? E o Cling, que executa o código c ++ sem compilá-lo. "e similares são interpretados (mesmo que usem etapas intermediárias como bytecode ou mesmo código nativo)" De acordo com isso, o java também é interpretado, interpretado por sua VM.
Abhinav Gauniyal