93

I am cautious of asking this question because it might appear overly fastidious. I just opened up JavaScript: The Definitive Guide, and it states of the first page of chapter 1

"JavaScript is a high-level, dynamic, untyped interpreted programming language”

So am I to take it that the interpreted part is a requirement in the language specification, or is it misleading to say that the language is an interpreted programming language when respecting the difference between a language and its many implementations?

There are no static compilers for JavaScript apparently - https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript so maybe it's just a reflection of this.

Matt Esch
  • 1,183
  • 1
  • 8
  • 10
  • There was a jscript.net for a while that was similar to AS3 / the "lost" ES4. It was bytecode-compiled to CIL. – Hey Mar 06 '12 at 15:53
  • 15
    V8 explicitly [claims](https://developers.google.com/v8/design#mach_code) not to be an interpreter but a compiler. – pimvdb Mar 06 '12 at 15:56
  • @GGG JScript.Net is still alive and...sickly. But still alive. http://msdn.microsoft.com/en-us/library/72bd815a.aspx – Jetti Mar 06 '12 at 16:18
  • 1
    FWIW, the "untyped" bit isn't strictly true either – Rob Agar Mar 06 '12 at 16:54
  • Firefox had just released the first browser-based JIT compiler the year that question was answered in FF 3.5 so it probably wasn't widely known about at the time. I believe modern JITs actually do a lot of compiling (or at least prep for compile) on the first pass of a JS document to do things like identify and cache methods that are isolated to a given scope. – Erik Reppen Jun 03 '13 at 18:45
  • https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch1.md – Adam Aug 18 '19 at 09:17

4 Answers4

61

So am I to take it that the interpreted part is a requirement in the language specification, or is it misleading to say that the language is an interpreted programming language when respecting the difference between a language and its many implementations?

EcmaScript language geeks often use the term "ES interpreter" to refer to an implementation of EcmaScript, but the spec does not use that term. The language overview in particular describes the language in interpreter-agnostic terms:

ECMAScript is object-based: basic language and host facilities are provided by objects, and an ECMAScript program is a cluster of communicating objects.

So EcmaScript assumes a "host environment" which is defined as a provider of object definitions including all those that allow I/O or any other links to the outside world, but does not require an interpreter.

The semantics of statements and expressions in the language are defined in terms of completion specification which are trivially implemented in an interpreter, but the specification does not require that.

8.9 The Completion Specification Type

The Completion type is used to explain the behaviour of statements (break, continue, return and throw) that perform nonlocal transfers of control. Values of the Completion type are triples of the form (type, value, target), where type is one of normal, break, continue, return, or throw, value is any ECMAScript language value or empty, and target is any ECMAScript identifier or empty.

The term “abrupt completion” refers to any completion with a type other than normal.

The non-local transfers of control can be converted to arrays of instructions with jumps allowing for native or byte-code compilation.

"EcmaScript Engine" might be a better way to express the same idea.


There are no static compilers for JavaScript apparently

This is not true. The V8 "interpreter" compiles to native code internally, Rhino optionally compiles to Java bytecode internally, and various Mozilla interpreters ({Trace,Spider,Jager}Monkey) use a JIT compiler.

V8:

V8 increases performance by compiling JavaScript to native machine code before executing it, versus executing bytecode or interpreting it.

Rhino:

public final void setOptimizationLevel(int optimizationLevel)

Set the current optimization level. The optimization level is expected to be an integer between -1 and 9. Any negative values will be interpreted as -1, and any values greater than 9 will be interpreted as 9. An optimization level of -1 indicates that interpretive mode will always be used. Levels 0 through 9 indicate that class files may be generated. Higher optimization levels trade off compile time performance for runtime performance. The optimizer level can't be set greater than -1 if the optimizer package doesn't exist at run time.

TraceMonkey:

TraceMonkey adds native‐code compilation to Mozilla’s JavaScript® engine (known as “SpiderMonkey”). It is based on a technique developed at UC Irvine called “trace trees”, and building on code and ideas shared with the Tamarin Tracing project. The net result is a massive speed increase both in the browser chrome and Web‐page content.

Mike Samuel
  • 1,840
  • 14
  • 19
  • 1
    Thanks for this answer, it actually answers the question. I suppose the final comment about no static compilation is what caused the buzz about which implementations actually compile code and which ones do not. All I was interested in was the validity of the statement "JavaScript is an interpreted language" which, given implementation citations and the lack of definition by the spec, appears to be false. Not encouraging for the second paragraph of a "Definitive Guide", but I guess I will stick with it. – Matt Esch Mar 06 '12 at 18:30
  • @me232, the statement was substantially true prior to 2008. Rhino pre-dates that but was not a major interpreter and so few would have faulted "the Definitive Guide" at the time for ignoring it. I haven't read the book, so I can't comment on how representative that sentence is of its overall quality. – Mike Samuel Mar 06 '12 at 20:58
  • What's the definition of "static compiler". I thought that definition meant compilation happens only once and you get a static (ie, unchanging) bucket of bits that you then execute. AFAIK this is not how any JavaScript engine works. That's why they have `de-optimization` steps. In other words JavaScript is compiled by these engines but it is not statically compiled. – gman Mar 29 '17 at 23:46
  • @gman, Rhino's bytecode generator works that way. – Mike Samuel Mar 30 '17 at 00:04
  • AFAIK that's not the case. Rhino can include other JavaScript files which have to be compiled at runtime. That's not *static* complication. – gman Mar 30 '17 at 00:54
  • @gman, Rhino can be used to compile just-in-time, but that is not it's only mode, and dynamic linking does not preclude static compilation. – Mike Samuel Mar 30 '17 at 01:05
  • Oh, ok. I will go try using Rhino. I'll dynamically link some JS that replaces low-level functions in Object's prototype and see rhino handles it. – gman Mar 30 '17 at 01:11
  • @gman What are you trying to accomplish. – Mike Samuel Mar 30 '17 at 01:13
  • to show that by definition JavaScript can not be statically compiled. – gman Mar 30 '17 at 01:51
  • @gman, it might be worth looking into [dyn.js](https://www.infoq.com/news/2011/10/dynjs) which aims to do "ahead of time" compilation of ES5 making extensive use of the *invokeDynamic* JVM bytecode instruction. – Mike Samuel Mar 30 '17 at 02:44
  • Sounds like a cool project that is dynamically compiling code at runtime not statically compiling code before runtime – gman Mar 30 '17 at 02:49
30

The emergence of JIT compilers for script languages has blurred the line between compilation and interpretation to a point where the question doesn't mean all that much. Is it only interpretation when the engine reads a line of code and immediately executes it? (Shell scripts are still typically implemented this way.) Is it interpretation when the engine takes the whole file, immediately compiles it to some byte code, and then interprets the byte code? (The first-stage Mozilla engine works this way, as does CPython.) Is it interpretation when the engine parses a function at a time and JIT-compiles it to native code? What about those engines that compile the entire file to byte code, then JIT one function at a time as needed? (Most script engines these days work this way, and the main Java VM works this way too except that compiling to byte code happens ahead of time.)

There are many shades between compilation and interpretation.

I think the most useful definition for interpretation is "is fed the source code of the program at execution time, without a separate ahead-of-time step". By this definition, all the JavaScript engines are interpreters. But this is certainly not the only possible definition of interpretation.

But is JavaScript designed for interpretation? In a way, yes: it has an eval function as well as the Function constructor that you can give program code as a string which will be executed. The ability to dynamically construct program code at run time requires that the engine is capable of interpreting source code. But this doesn't mean that you can't make everything else ahead-of-time. Even in a compiled language like C++ and C# you can take source code, compile it in memory to new machine code and then execute that. There are even libraries for that: LLVM+Clang in C++ and the Roslyn project in C#.

Also, the delivery mechanism for JavaScript is source code; there is no recognized byte code form of it. C# and Java have their official byte code, and everyone expects C++ to be delivered as machine code. But this is still not an inherent aspect if the language, just a dominant usage scenario. In fact, JavaScript's close relative ActionScript in Flash is in fact delivered as byte code (the Flash compiler precompiles all scripts).

Sebastian Redl
  • 14,950
  • 7
  • 54
  • 51
  • Now we have Hermes, an [AOT compiler](https://engineering.fb.com/2019/07/12/android/hermes/#:~:text=Bytecode%20precompilation,the%20mobile%20application%20build%20process.) for JS. – Minh Nghĩa Mar 23 '22 at 17:52
24

The V8 JavaScript VM used in Chrome doesn't include an interpreter. Instead it consists of two compilers and compiles the code on the fly. One of the compilers runs quickly but generates inefficient code, the other is an optimizing compiler.

I can understand why some people would consider this "cheating", since V8 takes source code as input every time the code runs and the user has to have V8 installed. But consider a compiler which emits an executable which includes a complete interpreter and bytecode. Then you would have a stand-alone program. It just wouldn't be very efficient.

Jørgen Fogh
  • 332
  • 1
  • 7
  • Nice answer. But I am wondering why the V8 engine has a two step compilation process? Why not compile the source code directly into machine code? Wouldn't that be faster? – darKnight Feb 23 '21 at 20:55
  • 1
    That is exactly what it does. It isn't really a two step process, it's more like two separate processes: One fast and one slow. The first process generates inefficient machine code quickly, while the second process generates optimized machine code for the hottest code paths. – Jørgen Fogh Mar 07 '21 at 16:12
  • I see. Could you share some doc which explains this process in detail? – darKnight Mar 07 '21 at 16:37
  • I just found out that what I wrote above has not been true since 2017. It seems that V8 now compiles to bytecode. See https://blog.logrocket.com/how-javascript-works-optimizing-the-v8-compiler-for-efficiency/ – Jørgen Fogh Mar 07 '21 at 17:10
8

There is no totally agreed upon definition of 'interpreted' versus 'compiled'. In the classic distinction, compiled languages produce a stand-alone binary executable, while interpreted languages requires a deployed runtime to execute the code. Virtual machines, bytecode and so on blurs the distinction.

But here is a possibly useful definition: An interpreted language is a language where the standard language runtime is able to take source code text as input and execute it. By that definition Perl, Python, Ruby, JavaScript and shell scripts and the like are interpreted (even if they use intermediate steps like bytecode or even native code). Java, C#, C etc. are not. And JavaScript is by definition interpreted, even if the spec does not use the exact word.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • 1
    Hmm, I don't like putting Java and C in the same category. Perhaps a better distinction is languages that are most commonly distributed as (A) source code, (B) intermediate code, or (C) machine code. For example A=javascript, B=Java, C=C. – John Henckel Dec 10 '15 at 17:39
  • 1
    Calling a language either interpreted or compiled isn't right. For example under that rule, you'd agree that C++ is compiled language right? Then what about Cling, that executes c++ code without compiling it. "and the like are interpreted (even if they use intermediate steps like bytecode or even native code)" Acc to this, java is interpreted too, interpreted by its VM. – Abhinav Gauniyal Sep 25 '16 at 13:03