Skip to content

Commit

Permalink
Introduce serde_v8 (denoland#9722)
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronO authored Mar 26, 2021
0 parents commit a4dc4ef
Show file tree
Hide file tree
Showing 14 changed files with 1,831 additions and 0 deletions.
16 changes: 16 additions & 0 deletions serde_v8/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "serde_v8"
version = "0.0.0"
authors = ["Aaron O'Mullan <aaron.omullan@gmail.com>"]
edition = "2018"


[dependencies]
serde = { version = "1.0.123", features = ["derive"] }
rusty_v8 = "0.21.0"

[dev-dependencies]
serde_json = "1.0.62"

[[example]]
name = "basic"
55 changes: 55 additions & 0 deletions serde_v8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# serde_v8

Serde support for encoding/decoding (rusty_)v8 values.

Broadly `serde_v8` aims to provide an expressive but ~maximally efficient
encoding layer to biject rust & v8/js values. It's a core component of deno's
op-layer and is used to encode/decode all non-buffer values.

**Original issue:**
[denoland/deno#9540](https://github.com/denoland/deno/issues/9540)

## Quickstart

`serde_v8` fits naturally into the serde ecosystem, so if you've already used
`serde` or `serde_json`, `serde_v8`'s API should be very familiar.

`serde_v8` exposes two key-functions:

- `to_v8`: maps `rust->v8`, similar to `serde_json::to_string`, ...
- `from_v8`: maps `v8->rust`, similar to `serde_json::from_str`, ...

## Best practices

Whilst `serde_v8` is compatible with `serde_json::Value` it's important to keep
in mind that `serde_json::Value` is essentially a loosely-typed value (think
nested HashMaps), so when writing ops we recommend directly using rust
structs/tuples or primitives, since mapping to `serde_json::Value` will add
extra overhead and result in slower ops.

I also recommend avoiding unecessary "wrappers", if your op takes a single-keyed
struct, consider unwrapping that as a plain value unless you plan to add fields
in the near-future.

Instead of returning "nothing" via `Ok(json!({}))`, change your return type to
rust's unit type `()` and returning `Ok(())`, `serde_v8` will efficiently encode
that as a JS `null`.

## Advanced features

If you need to mix rust & v8 values in structs/tuples, you can use the special
`serde_v8::Value` type, which will passthrough the original v8 value untouched
when encoding/decoding.

## TODO

- [ ] Experiment with KeyCache to optimize struct keys
- [ ] Experiment with external v8 strings
- [ ] Explore using
[json-stringifier.cc](https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/json/json-stringifier.cc)'s
fast-paths for arrays
- [ ] Improve tests to test parity with `serde_json` (should be mostly
interchangeable)
- [ ] Consider a `Payload` type that's deserializable by itself (holds scope &
value)
- [ ] Ensure we return errors instead of panicking on `.unwrap()`s
57 changes: 57 additions & 0 deletions serde_v8/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use rusty_v8 as v8;

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct MathOp {
pub a: u64,
pub b: u64,
pub operator: Option<String>,
}

fn main() {
let platform = v8::new_default_platform().unwrap();
v8::V8::initialize_platform(platform);
v8::V8::initialize();

{
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
let handle_scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(handle_scope);
let scope = &mut v8::ContextScope::new(handle_scope, context);

fn exec<'s>(
scope: &mut v8::HandleScope<'s>,
src: &str,
) -> v8::Local<'s, v8::Value> {
let code = v8::String::new(scope, src).unwrap();
let script = v8::Script::compile(scope, code, None).unwrap();
script.run(scope).unwrap()
}

let v = exec(scope, "32");
let x32: u64 = serde_v8::from_v8(scope, v).unwrap();
println!("x32 = {}", x32);

let v = exec(scope, "({a: 1, b: 3, c: 'ignored'})");
let mop: MathOp = serde_v8::from_v8(scope, v).unwrap();
println!("mop = {:?}", mop);

let v = exec(scope, "[1,2,3,4,5]");
let arr: Vec<u64> = serde_v8::from_v8(scope, v).unwrap();
println!("arr = {:?}", arr);

let v = exec(scope, "['hello', 'world']");
let hi: Vec<String> = serde_v8::from_v8(scope, v).unwrap();
println!("hi = {:?}", hi);

let v: v8::Local<v8::Value> = v8::Number::new(scope, 12345.0).into();
let x: f64 = serde_v8::from_v8(scope, v).unwrap();
println!("x = {}", x);
}

unsafe {
v8::V8::dispose();
}
v8::V8::shutdown_platform();
}
Loading

0 comments on commit a4dc4ef

Please sign in to comment.