This is the 14th day of my participation in Gwen Challenge. For details, see Gwen Challenge.

How does JavaScript work

High-level and low-level languages

If you pick a random traveler and ask him what language you know, he will probably answer in English, Chinese, German, and so on. But if you asked a programmer this question, I think his first answer would be Java, PHP, etc. In the field of computer, we call Chinese, English, Java, GO and so on all languages, and the language biased to hardware become low-level languages. We can use low-level language to control the behavior of the hardware, the language biased to human, that is, human easy to understand language called high-level language, we can use human easier to read, easier to understand the high-level language to indirectly control the behavior of the hardware. The lowest level of language is machine language, and the highest level is human language. High-level languages are easy to read and write, simple and abstract, but have poor execution ability. It requires gradual transposition and low-level languages to be used by hardware. The higher the level, the longer the conversion time is, and the lower the efficiency is. Lower-level languages execute faster, but the lack of language convenience makes it difficult to write code that is error-prone, such as memory management, prone to memory leaks, difficult to track and resolve problems, and incompatible with many CPU platforms. JavaScript, the subject of this issue, is a high-level language: it draws on the basic syntax of C, Java’s memory management and data typing, Scheme’s functional programming, and Self’s prototypical inheritance.

JavaScript engine

Since JavaScript is a high-level complementary element, it needs to install JS into the language that the computer knows and execute it in some form before it is executed by the computer. This program is called JavaScript engine. For example, Chrome’s V8 engine, WebKit is used by JavaScriptCore, etc. Although there are many kinds of parsers, they all have certain similarities: the AST parses JS source code into abstract syntax, and the AST is converted into bytecode by the interpreter, and the bytecode is converted into different machine code by the compiler. Because different platforms use different machine code, the compiler compiles the corresponding machine code for the current platform. There may be some differences in the performance of compilations in different JS engines, such as V8, which we will cover next.

How does the V8 engine compile and optimize computer code

First, a definition: V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C ++. It works in Chrome, Node.js, etc. It implements ECMAScript and WebAssembly and runs on Windows 7 or later, macOS 10.12+, and Linux systems using X64, IA-32, ARM, or MIPS processors. V8 can run on its own or be embedded in any C ++ application. V8 mainly does the following work:

  • – Compile and execute JS code.
  • Handle the call stack.
  • Memory allocation.
  • The garbage collection

Most JS engines use parsers, interpreters, and compiler methods when parsing JS, as I mentioned above. But in the early V8 engines there was no interpreter but two compilers, and the flow looked something like this:

JS is parsed by the parser to generate AST syntax tree and then directly compiled by full-codeGen as long as the AST is compiled into machine code without any code conversion. Full-codegen is also known as a benchmark compiler because it generates benchmark, unoptimized code. The advantage of this is that when you first execute JS code, you directly use machine code, and you don’t only see bytecode generation, so you don’t need an interpreter. After the code had been running for a while, the V8 engine collected enough data to help the other compiler crankshaft optimize the JS code, which had the molding build AST syntax, and their crankshaft crankshaft used the generated AST to regenerate the optimized machine code for increased efficiency.

The original intention of this design is to reduce the abstract syntax tree to bytecode conversion time, improve the efficiency of JS running in the browser. However, such an architectural design results in large machine code and large memory footprint. Without intermediate code, the optimization strategy cannot be implemented. New features that do not support the new JS syntax well. Hence the new V8 engine. The execution mechanism of the new V8 engine

The parsing of the grammar tree is still consistent, but after obtaining the abstract grammar tree, the interpreter Lgnition adds it to generate bytecode through Lgntition, and then the AST is clear and free up space. And the generated bytecode will be executed directly At the same time of execution Will also be implemented as a benchmark model, the bytecode is more abstract and at the same time generate bytecode size of 25% ~ 50% of equivalent benchmark machine code, the code is running in the process of the interpreter collected can be used to optimize code information, such as the type of the variable, When those functions are executed more frequently, this information is sent to the compiler TurboFan, which compiles optimized machine code based on this information and bytecode