Top Related Projects
The official mirror of the V8 Git repository
Servo, the embeddable, independent, memory-safe, modular, parallel web rendering engine
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
Node.js JavaScript runtime ✨🐢🚀✨
A lightweight WebAssembly runtime that is fast, secure, and standards-compliant
The zero configuration build tool for the web. 📦🚀
Quick Overview
The denoland/rusty_v8
repository is a Rust binding for the V8 JavaScript engine, which is the core of the Deno runtime. This project allows developers to embed the V8 engine into their Rust applications, enabling them to execute and interact with JavaScript code from within their Rust programs.
Pros
- High Performance: The V8 engine is known for its high-performance JavaScript execution, which is inherited by the Rust binding.
- Extensive Functionality: The V8 engine provides a wide range of features and functionality, which are accessible through the Rust binding.
- Interoperability: The Rust binding allows for seamless integration between Rust and JavaScript, enabling developers to leverage the strengths of both languages.
- Active Development: The
denoland/rusty_v8
project is actively maintained and developed, with regular updates and improvements.
Cons
- Complexity: Integrating the V8 engine into a Rust application can be a complex task, requiring a deep understanding of both Rust and the V8 engine.
- Dependency Management: The
denoland/rusty_v8
project has a significant number of dependencies, which can make it challenging to manage and update. - Compatibility: The Rust binding may not always be compatible with the latest version of the V8 engine, which can lead to compatibility issues.
- Learning Curve: Developers new to Rust or the V8 engine may face a steeper learning curve when working with the
denoland/rusty_v8
project.
Code Examples
Here are a few code examples demonstrating the usage of the denoland/rusty_v8
project:
- Executing JavaScript Code:
use rusty_v8::{Context, Isolate, JsString, JsValue};
fn main() {
let mut isolate = Isolate::new(Default::default());
let context = Context::new(&mut isolate);
let result: JsValue = context.eval("2 + 2", "").unwrap();
let value: i64 = result.into_smi().unwrap();
println!("Result: {}", value); // Output: Result: 4
}
- Calling Rust Functions from JavaScript:
use rusty_v8::{Context, Isolate, JsFunction, JsString, JsValue};
fn add(mut ctx: rusty_v8::FunctionContext) -> rusty_v8::JsResult<rusty_v8::JsValue> {
let a: i64 = ctx.get::<_, JsValue>(0)?.into_smi()?;
let b: i64 = ctx.get::<_, JsValue>(1)?.into_smi()?;
Ok(JsValue::from_smi(a + b))
}
fn main() {
let mut isolate = Isolate::new(Default::default());
let context = Context::new(&mut isolate);
let add_function = JsFunction::new(&mut context, add).unwrap();
let result: JsValue = add_function.call(&mut context, &[2.into(), 3.into()]).unwrap();
let value: i64 = result.into_smi().unwrap();
println!("Result: {}", value); // Output: Result: 5
}
- Accessing JavaScript Objects:
use rusty_v8::{Context, Isolate, JsObject, JsString, JsValue};
fn main() {
let mut isolate = Isolate::new(Default::default());
let context = Context::new(&mut isolate);
let obj: JsObject = JsObject::new(&mut context);
obj.set(&mut context, "name", JsString::new(&mut context, "John").unwrap()).unwrap();
obj.set(&mut context, "age", JsValue::from_smi(30)).unwrap();
let name: JsString = obj.get(&mut context, "name").unwrap().into();
let age: i64 = obj
Competitor Comparisons
The official mirror of the V8 Git repository
Pros of v8/v8
- Mature and widely-used JavaScript engine, with a large and active community
- Extensive documentation and resources available for developers
- Supports a wide range of platforms and architectures
Cons of v8/v8
- Larger codebase and more complex to build and maintain
- May have higher resource requirements compared to Rusty_V8
- Potentially less flexible for specific use cases or integration with Rust-based projects
Code Comparison
v8/v8:
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
denoland/rusty_v8:
let isolate = Isolate::new(IsolateOptions::default());
Servo, the embeddable, independent, memory-safe, modular, parallel web rendering engine
Pros of Servo
- Servo is a more mature and feature-rich browser engine, with support for a wide range of web technologies and standards.
- Servo has a strong focus on parallelism and performance, which can lead to faster rendering and better responsiveness.
- Servo has a larger and more active community, with contributions from a wide range of developers.
Cons of Servo
- Servo is a larger and more complex project, which can make it more difficult to set up and contribute to.
- Servo's development is primarily focused on the browser engine, rather than on a specific use case like Rusty V8.
- Servo's performance and stability may not be as consistent as Rusty V8, especially for specific use cases.
Code Comparison
Here's a brief code comparison between Servo and Rusty V8:
Servo (Rust):
let mut context = ServoContext::new();
let mut document = Document::new(&mut context);
document.set_url(Url::parse("https://example.com").unwrap());
document.set_content(r#"<html><body>Hello, world!</body></html>"#);
document.reflow();
Rusty V8 (Rust):
let mut isolate = Isolate::new(Default::default());
let mut context = Context::new(&mut isolate);
let source = r#"console.log('Hello, world!');"#;
let script = Script::compile(&mut context, source, None).unwrap();
script.run(&mut context).unwrap();
Both examples demonstrate the basic usage of the respective libraries, but Servo's code is more focused on the browser engine, while Rusty V8's code is more focused on the JavaScript runtime.
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
Pros of grpc/grpc
- Supports a wide range of programming languages, including C++, Java, Python, and more, making it a versatile choice for cross-platform development.
- Provides a robust and efficient RPC (Remote Procedure Call) framework for building distributed applications.
- Offers advanced features like load balancing, tracing, and authentication, which can simplify the development of complex distributed systems.
Cons of grpc/grpc
- Steeper learning curve compared to simpler RPC frameworks, as it requires understanding of protocol buffers and the gRPC protocol.
- May have higher overhead and complexity compared to more lightweight communication mechanisms, depending on the specific use case.
- Requires additional tooling and setup, such as the gRPC compiler, which can add complexity to the development process.
Code Comparison
Here's a brief code comparison between grpc/grpc and denoland/rusty_v8:
grpc/grpc (C++ example):
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
class GreeterServiceImpl final : public helloworld::Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
denoland/rusty_v8 (Rust example):
use rusty_v8::Context;
fn main() {
let mut isolate = rusty_v8::Isolate::new(Default::default());
let mut context = Context::new(&mut isolate);
let source = r#"
console.log('Hello, World!');
"#;
context.eval(source).unwrap();
}
Node.js JavaScript runtime ✨🐢🚀✨
Pros of Node.js
- Vast ecosystem of libraries and tools available through npm, the Node.js package manager.
- Extensive community support and a large number of active contributors.
- Mature and well-documented, with a wealth of resources and tutorials available.
Cons of Node.js
- Relies on the V8 JavaScript engine, which may not be as performant as a dedicated runtime like Rust's Rusty_V8.
- Potential for memory leaks and other performance issues, especially in long-running server applications.
Code Comparison
Node.js:
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
}).listen(8000);
console.log('Server running at http://localhost:8000/');
Rusty_V8:
use rusty_v8::{Context, EscapableHandleScope, HandleScope, Local, Value};
fn main() {
let mut isolate = rusty_v8::new_isolate();
let mut scope = HandleScope::new(&mut isolate);
let context = Context::new(&mut scope);
let mut scope = EscapableHandleScope::new(&mut scope);
let global = context.global(&mut scope);
global.set(&mut scope, "message", &mut scope, Value::new_string(&mut scope, "Hello, World!")).unwrap();
}
A lightweight WebAssembly runtime that is fast, secure, and standards-compliant
Pros of Wasmtime
- WebAssembly Support: Wasmtime is a WebAssembly runtime that supports the latest WebAssembly specification, allowing for the execution of a wide range of WebAssembly modules.
- Performance: Wasmtime is designed for high-performance execution of WebAssembly, with a focus on optimizing the runtime for speed and efficiency.
- Portability: Wasmtime is built to be cross-platform, with support for various operating systems and architectures, making it a versatile choice for developers.
Cons of Wasmtime
- Limited Language Support: While Wasmtime supports WebAssembly, it does not provide the same level of language support as Rusty_V8, which is primarily focused on the V8 JavaScript engine.
- Ecosystem: Rusty_V8 benefits from the large and active JavaScript/Node.js ecosystem, while Wasmtime's ecosystem may be smaller and less mature.
Code Comparison
Rusty_V8 (JavaScript execution):
let context = v8::Context::new(&isolate);
context.enter();
let source = v8::String::new(&isolate, "console.log('Hello, World!')").unwrap();
let script = v8::Script::compile(&context, source).unwrap();
script.run(&context).unwrap();
Wasmtime (WebAssembly execution):
let engine = Engine::default();
let module = Module::from_file(&engine, "example.wasm")?;
let instance = Instance::new(&module, &[])?;
let result = instance.get_export("add")?.get_func().unwrap().call(&[10.into(), 32.into()])?;
println!("Result: {}", result.unwrap().i32());
The zero configuration build tool for the web. 📦🚀
Pros of Parcel
- Ease of Use: Parcel is known for its simplicity and ease of use, making it a popular choice for developers who want to quickly set up a development environment without the need for complex configuration.
- Fast Bundling: Parcel utilizes a file watcher and a cache system to provide fast bundling and rebuilding, which can improve development workflow.
- Automatic Optimization: Parcel automatically optimizes the output, including code splitting, minification, and tree shaking, without the need for manual configuration.
Cons of Parcel
- Limited Customization: Parcel's focus on simplicity and ease of use can also be a limitation, as it may not provide the same level of customization and control as other bundlers like Webpack.
- Dependency on Node.js: Parcel is built on Node.js, which means that developers who are not familiar with the Node.js ecosystem may face a steeper learning curve.
- Potential Performance Issues: In some cases, Parcel's automatic optimization and bundling may not be as efficient as a more manual approach, leading to potential performance issues.
Code Comparison
Parcel:
const app = document.getElementById('app');
app.innerHTML = 'Hello, Parcel!';
Rusty V8:
fn main() {
let mut isolate = v8::Isolate::new(Default::default());
let mut scope = v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(&mut scope);
let mut scope = v8::ContextScope::new(&mut scope, context);
println!("Hello, Rusty V8!");
}
In this comparison, Parcel provides a more concise and straightforward approach to setting up a simple web application, while Rusty V8 demonstrates the more complex and lower-level nature of working with the V8 JavaScript engine directly in Rust.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Rusty V8 Binding
V8 Version: 13.0.245.12
Goals
-
Provide high quality Rust bindings to V8's C++ API. The API should match the original API as closely as possible.
-
Do not introduce additional call overhead. (For example, previous attempts at Rust V8 bindings forced the use of Persistent handles.)
-
Do not rely on a binary
libv8.a
built outside of cargo. V8 is a very large project (over 600,000 lines of C++) which often takes 30 minutes to compile. Furthermore, V8 relies on Chromium's bespoke build system (gn + ninja) which is not easy to use outside of Chromium. For this reason many attempts to bind to V8 rely on pre-built binaries that are built separately from the binding itself. While this is simple, it makes upgrading V8 difficult, it makes CI difficult, it makes producing builds with different configurations difficult, and it is a security concern since binary blobs can hide malicious code. For this reason we believe it is imperative to build V8 from source code during "cargo build". -
Publish the crate on crates.io and allow docs.rs to generate documentation. Due to the complexity and size of V8's build, this is nontrivial. For example the crate size must be kept under 10 MiB in order to publish.
Versioning
Rusty V8's major version aligns with Chrome's major version, which corresponds
to a specific V8 release. For example, Rusty V8 129.0.0
maps to Chrome
129.x.y.z
, which uses V8 12.9.a.b
. While the minor and patch numbers between
Chrome and V8 may differ, Rusty V8 will follow Chrome's release schedule, with a
new major version every 4 weeks.
As a Rust crate, Rusty V8 follows semantic versioning (semver) and will not introduce breaking changes within a major version. However, major version bumps will occur regularly to stay in sync with Chrome's release cycle.
Binary Build
V8 is very large and takes a long time to compile. Many users will prefer to use a prebuilt version of V8. We publish static libs for every version of rusty v8 on Github.
Binaries builds are turned on by default: cargo build
will initiate a download
from github to get the static lib. To disable this build using the
V8_FROM_SOURCE
environmental variable.
When making changes to rusty_v8 itself, it should be tested by build from source. The CI always builds from source.
The V8_FORCE_DEBUG
environment variable
By default rusty_v8
will link against release builds of v8
, if you want to
use a debug build of v8
set V8_FORCE_DEBUG=true
.
We default to release builds of v8
due to performance & CI reasons in deno
.
The RUSTY_V8_MIRROR
environment variable
Tells the build script where to get binary builds from. Understands http://
and https://
URLs, and file paths. The default is
https://github.com/denoland/rusty_v8/releases.
File-based mirrors are good for using cached downloads. First, point the environment variable to a suitable location:
# you might want to add this to your .bashrc
$ export RUSTY_V8_MIRROR=$HOME/.cache/rusty_v8
Then populate the cache:
#!/bin/bash
# see https://github.com/denoland/rusty_v8/releases
for REL in v0.13.0 v0.12.0; do
mkdir -p $RUSTY_V8_MIRROR/$REL
for FILE in \
librusty_v8_debug_x86_64-unknown-linux-gnu.a \
librusty_v8_release_x86_64-unknown-linux-gnu.a \
; do
if [ ! -f $RUSTY_V8_MIRROR/$REL/$FILE ]; then
wget -O $RUSTY_V8_MIRROR/$REL/$FILE \
https://github.com/denoland/rusty_v8/releases/download/$REL/$FILE
fi
done
done
The RUSTY_V8_ARCHIVE
environment variable
Tell the build script to use a specific v8 library. This can be an URL or a path. This is useful when you have a prebuilt archive somewhere:
export RUSTY_V8_ARCHIVE=/path/to/custom_archive.a
cargo build
Build V8 from Source
Use V8_FROM_SOURCE=1 cargo build -vv
to build the crate completely from
source.
The build scripts require Python 3 to be available as python3
in your PATH
.
If you want to specify the exact binary of Python to use, you should use the
PYTHON
environment variable.
The build also requires curl
to be installed on your system.
For linux builds: glib-2.0 development files need to be installed such that
pkg-config can find them. On Ubuntu, run sudo apt install libglib2.0-dev
to
install them.
For Windows builds: the 64-bit toolchain needs to be used. 32-bit targets are not supported.
For Mac builds: You'll need Xcode and Xcode CLT installed. Recent macOS versions
will also require you to pass PYTHON=python3 because macOS no longer ships with
python
simlinked to Python 3.
For Android builds: You'll need to cross compile from a x86_64 host to the aarch64 or x64 android. You can use the following commands:
rustup target add aarch64-linux-android # or x86_64-linux-android
V8_FROM_SOURCE=1 cargo build -vv --target aarch64-linux-android
# or with cross
docker build --build-arg CROSS_BASE_IMAGE=ghcr.io/cross-rs/aarch64-linux-android:0.2.5 -t cross-rusty_v8:aarch64-linux-android .
V8_FROM_SOURCE=1 cross build -vv --target aarch64-linux-android
The build depends on several binary tools: gn
, ninja
and clang
. The tools
will automatically be downloaded, if they are not detected in the environment.
Specifying the $GN
and $NINJA
environmental variables can be used to skip
the download of gn and ninja. The clang download can be skipped by setting
$CLANG_BASE_PATH
to the directory containing a llvm
/clang
installation. V8
is known to rely on bleeding edge features, so LLVM v8.0+ or Apple clang 11.0+
is recommended.
Arguments can be passed to gn
by setting the $GN_ARGS
environmental
variable.
Env vars used in when building from source: SCCACHE
, CCACHE
, GN
, NINJA
,
CLANG_BASE_PATH
, GN_ARGS
FAQ
Building V8 takes over 30 minutes, this is too slow for me to use this crate. What should I do?
Install sccache or
ccache. Our build scripts will detect and use them. Set
the $SCCACHE
or $CCACHE
environmental variable if it's not in your path.
What are all these random directories for like build
and buildtools
are
these really necessary?
In order to build V8 from source code, we must provide a certain directory structure with some git submodules from Chromium. We welcome any simplifications to the code base, but this is a structure we have found after many failed attempts that carefully balances the requirements of cargo crates and GN/Ninja.
V8 has a very large API with hundreds of methods. Why don't you automate the generation of this binding code?
We have actually started down this route several times, however due to many eccentric features of the V8 API, this has not proven successful. Therefore we are proceeding in a brute-force fashion for now, focusing on solving our stated goals first. We hope to auto-generate bindings in the future.
Why are you building this?
This is to support the Deno project. We previously have gotten away with a simpler high-level Rust binding to V8 called libdeno. But as Deno has matured we've found ourselves continually needing access to an increasing amount of V8's API in Rust.
When building I get unknown argument: '-gno-inline-line-tables'
Use export GN_ARGS="no_inline_line_tables=false"
during build.
My program crashes when initializing on non-main thread
Initializing V8 on a non-main thread with the CPUs PKU feature enabled might
lead to crashes. You can work around this problem by using
v8::new_unprotected_default_platform
.
See https://github.com/denoland/rusty_v8/issues/1381
Download cache
The v8 archives used for linking in prebuilt mode can be cached to avoid re-downloading archives when switching between branches that otherwise change the current rusty_v8 version.
To populate the cache by hand, you'll need to place the files in the appropriate
location in your .cargo
folder. Running cargo build -v -v
will print two
lines that you can use to determine the correct file and cache location:
[v8 0.87.0] static lib URL: https://github.com/denoland/rusty_v8/releases/download/v0.87.0/librusty_v8_release_aarch64-apple-darwin.a.gz
[v8 0.87.0] Looking for download in '"/Users/<name>/.cargo/.rusty_v8/https___github_com_denoland_rusty_v8_releases_download_v0_87_0_librusty_v8_release_aarch64_apple_darwin_a_gz"'
Given the above log output, use curl
to download the file like so:
curl -L https://github.com/denoland/rusty_v8/releases/download/v0.87.0/librusty_v8_release_aarch64-apple-darwin.a.gz >
/Users/<name>/.cargo/.rusty_v8/https___github_com_denoland_rusty_v8_releases_download_v0_87_0_librusty_v8_release_aarch64_apple_darwin_a_gz
For maintainers
Cut a release
Create a PR to bump the release version (e.g. https://github.com/denoland/rusty_v8/pull/1415).
Create a new release/tag after the bump PR is landed. CI will publish the crate and upload release binaries. You will need to manually upload binary archives for M1 build.
$ V8_FROM_SOURCE=1 cargo build
$ V8_FROM_SOURCE=1 cargo build --release
Top Related Projects
The official mirror of the V8 Git repository
Servo, the embeddable, independent, memory-safe, modular, parallel web rendering engine
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
Node.js JavaScript runtime ✨🐢🚀✨
A lightweight WebAssembly runtime that is fast, secure, and standards-compliant
The zero configuration build tool for the web. 📦🚀
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot