4.1 The bridge

Overview

The Deno runtime utilizes Google's v8 engine to execute JavaScript code that it fetches and loads. However, Deno's responsibilities don't end once the code is within the v8 engine. It can't simply step back and let v8 handle everything. This is a significant "no."

The v8 engine has a strict focus – it only supports the features outlined in the ECMAScript specification. Any functionality that falls beyond this specification becomes the responsibility of the v8 engine's user, not Deno.

To illustrate, consider the basic console.log function. It's not inherently a part of the ECMAScript specification. Consequently, whoever uses the v8 engine must implement this function themselves. Similarly, tasks like making HTTP calls to servers aren't within the realm of the ECMAScript specification. Therefore, the responsibility for implementing such functionality also rests on the user of the v8 engine.

A variety of operations fall outside the scope of the ECMAScript specification, such as dealing with files, managing processes, handling network communications, and performing system-level tasks. In essence, anything that extends beyond the bounds of the ECMAScript specification requires the user to provide support for it within the v8 engine.

In summary, it's crucial to comprehend that the v8 engine relies on the user for assistance with anything beyond what's detailed in the ECMAScript specification. Deno's role continues even after code is loaded into v8. It involves facilitating the implementation of these additional functionalities, making the whole system functional and versatile.

V8 <-> Deno

The JavaScript application runs using the v8 engine. Deno's primary function is to offer assistance for all the aspects that exist beyond this specific engine's capabilities.

Imagine the connection between the v8 user (which is Deno in this case) and the v8 engine as a bridge. This analogy arises from the distinctions between v8 and Deno. This bridge functions in both directions, resembling a two-way passage. It is important to note that there exists a predetermined way to traverse this bridge while ensuring a smooth round trip. Just like a bridge, Deno acts as a facilitator, allowing the JavaScript code to access functionalities beyond the v8 engine's confines and return seamlessly. This interaction forms an essential part of Deno's role in enhancing the capabilities of the JavaScript application.

In the realm of programming, traversing the bridge bears resemblance to making a function call, albeit with a twist. This unique juncture is referred to as a "bridge" due to the distinct nature of both v8 and Deno. It's essential to grasp that Deno and v8 exist as separate entities, each possessing its own identity. To delve deeper, Deno stands as an autonomous software entity crafted using Rust, while v8 charts its own course as an entirely distinct C++ entity.

Handling (un)known functions

Using external functions follows a straightforward logic. Whenever the V8 engine encounters something that lies beyond the boundaries of the ECMAScript specification, it takes specific actions:

The V8 algorithm outlines the steps it takes when encountering a function call. Here's a breakdown in simpler terms:

When a function is called:

  1. First, the algorithm looks at the name of the function in its internal data records.

  2. If the algorithm recognizes the function (meaning it's a function that follows the rules of ECMAScript and is supported by V8), it proceeds to execute that function.

  3. But if the function isn't recognized by the algorithm, it moves to the next step.

  4. In this case, if the user has given V8 information about an external function in advance, the algorithm temporarily stops its current process.

  5. It then calls the external function provided by the user.

  6. The algorithm waits until a response is received from this external function.

  7. Once the response is ready, the algorithm resumes its work using the obtained response.

  8. If no external function is registered or if the response doesn't come through, the algorithm raises an error to indicate the issue.

These external functions that the algorithm can use are known as external references. Beforehand, these references must be added to the V8's knowledge. This ensures that V8 is aware of these external functions and can use them as needed during its execution. This registration step is important for smooth interaction between the algorithm and external code.

Registration of external references

During its initialization process, Deno systematically records all external references with the v8 engine. This crucial step guarantees that v8 has these references properly set up whenever the fundamental Deno runtime is activated. Below, you will find a compilation of some of the external references that Deno registers, contributing to the seamless functioning of the runtime.

  • Built-in OPs (all the OPs are the basic built-in OPs)

  • Extension OPs (These OPs are provided by extensions)

  • import_meta_resolve

  • catch_dynamic_import_promise_error

  • wasm_async_resolve_promise_callback

  • host_import_module_dynamically_callback

  • host_initialize_import_meta_object_callback

  • import_meta_resolve

  • empty_fn

  • catch_dynamic_import_promise_error

  • etc.

Below is a code snippet that illustrates how external functions are registered with the V8 engine:

references.push(v8::ExternalReference {
  function: call_console.map_fn_to(),
});
references.push(v8::ExternalReference {
  function: import_meta_resolve.map_fn_to(),
});
references.push(v8::ExternalReference {
  function: catch_dynamic_import_promise_error.map_fn_to(),
});
references.push(v8::ExternalReference {
  function: empty_fn.map_fn_to(),
});

Ops

In Deno, aside from just registering functions like the promise reject callback and module resolve callback, there's another noteworthy function it performs. Deno takes the step of registering all the OPs (short for operations) with V8, the underlying JavaScript engine it uses.

for ctx in ops {
    let ctx_ptr = ctx as *const OpCtx as _;
    references.push(v8::ExternalReference { pointer: ctx_ptr });
    references.push(v8::ExternalReference {
      function: ctx.decl.v8_fn_ptr,
    });
    if let Some(fast_fn) = &ctx.decl.fast_fn {
      references.push(v8::ExternalReference {
        pointer: fast_fn.function as _,
      });
      references.push(v8::ExternalReference {
        pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
      });
    }
}

--

Let's now explore some frequently utilized external references in the upcoming sections.

Last updated