2.6 V8
V8 is the most important library used by Deno. Well, tokio is also important. But v8 is where the Javascript code runs. Without v8, Deno can't function.
V8 is Google’s open-source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome, Node.js, and now Deno. It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linux systems that use x64, IA-32, ARM, or MIPS processors. V8 can run standalone or can be embedded into any C++ application.
At a minimum, V8 compiles and executes JavaScript source code. Also, it handles memory allocation for objects, and garbage collects objects it no longer needs. Garbage collection is where v8 stands out from its competition. V8’s accurate garbage collector is one of the keys to V8’s performance.
V8 enables any C++ application to expose its own objects and functions to JavaScript code. It’s up to the application to decide on the objects and functions it would like to expose to JavaScript.
V8 is very big. It has around a million lines of code. V8 has a very big public API. We can't practically go over this very complicated JS engine. We'll just touch upon some of the key concepts.
Consider basic hello world program (ref: https://chromium.googlesource.com/v8/v8/+/branch-heads/6.8/samples/hello-world.cc)
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
// Initialize V8.
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source =
v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'",
v8::NewStringType::kNormal)
.ToLocalChecked();
// Compile the source code.
v8::Local<v8::Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return 0;
}
This simple program just prints a string hello world. Though it's very simple, the hello world program offers a window into some of the key v8 concepts. Let's go through them one by one.
Isolate is an isolated instance of the V8 engine. V8 isolates have completely separate states. Objects from one isolate must not be used in other isolates. When V8 is initialized a default isolate is implicitly created and entered. The embedder can create additional isolates and use them in parallel in multiple threads. An isolate can be entered by at most one thread at any given time.
Isolates are like VM with their own heap. Isolate initialization is always the first step.
In the world of v8, a context is an execution environment that allows separate, unrelated JS application to run in a single instance of v8. A context needs to be specified by a user program to run a JS code.
When you have created a context you can enter and exit it any number of times. While you are in context A you can also enter a different context, B, which means that you replace A as the current context with B. When you exit B then A is restored as the current context.
The motivation for using contexts in V8 was so that each window and iframe in a browser can have its own fresh JavaScript environment.
The next two steps are compilation and execution of the JS code. Well, an obvious question is: why there is a compilation? Isn't JS interpreted? JS is interpreted, but v8 does a level of compilation. The purpose of the compilation step is to increase the speed of execution.
The V8 engine gets its speed from the Just in Time (JIT) compilation of JS code to native machine code. On runtime, the machine code is analyzed and re-compiled for optimal performance.
That was all about v8. V8 is way too complex to discuss in a small section. V8 needs a book of its own.