mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
chore: move serde_v8 to separate repo (#10909)
Now available at https://github.com/denoland/serde_v8
This commit is contained in:
parent
e779ea9565
commit
e75ffab0c8
25 changed files with 3 additions and 2829 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2906,11 +2906,11 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_v8"
|
name = "serde_v8"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d1c602ced78c18b33338c832d79595e75ac6fd204d20fd4cc462ce5af29a75e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bencher",
|
|
||||||
"rusty_v8",
|
"rusty_v8",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -6,7 +6,6 @@ members = [
|
||||||
"cli",
|
"cli",
|
||||||
"core",
|
"core",
|
||||||
"runtime",
|
"runtime",
|
||||||
"serde_v8",
|
|
||||||
"test_plugin",
|
"test_plugin",
|
||||||
"test_util",
|
"test_util",
|
||||||
"extensions/broadcast_channel",
|
"extensions/broadcast_channel",
|
||||||
|
|
|
@ -13,8 +13,6 @@ repository = "https://github.com/denoland/deno"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_v8 = { version = "0.4.1", path = "../serde_v8" }
|
|
||||||
|
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
indexmap = "1.6.2"
|
indexmap = "1.6.2"
|
||||||
|
@ -25,6 +23,7 @@ pin-project = "1.0.6"
|
||||||
rusty_v8 = "0.22.3"
|
rusty_v8 = "0.22.3"
|
||||||
serde = { version = "1.0.125", features = ["derive"] }
|
serde = { version = "1.0.125", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.64", features = ["preserve_order"] }
|
serde_json = { version = "1.0.64", features = ["preserve_order"] }
|
||||||
|
serde_v8 = { version = "0.4.1" }
|
||||||
url = { version = "2.2.1", features = ["serde"] }
|
url = { version = "2.2.1", features = ["serde"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
[package]
|
|
||||||
name = "serde_v8"
|
|
||||||
version = "0.4.1"
|
|
||||||
authors = ["the Deno authors"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "Rust to V8 serialization and deserialization"
|
|
||||||
license = "MIT"
|
|
||||||
readme = "README.md"
|
|
||||||
repository = "https://github.com/denoland/deno"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1.0.125", features = ["derive"] }
|
|
||||||
rusty_v8 = "0.22.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
serde_json = "1.0.64"
|
|
||||||
bencher = "0.1"
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "basic"
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "de"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "ser"
|
|
||||||
harness = false
|
|
|
@ -1,57 +0,0 @@
|
||||||
# serde_v8
|
|
||||||
|
|
||||||
Author: Aaron O'Mullan <aaron.omullan@gmail.com>
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,160 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
|
||||||
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use serde_v8::utils::{js_exec, v8_do};
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
|
||||||
struct MathOp {
|
|
||||||
arg1: u64,
|
|
||||||
arg2: u64,
|
|
||||||
operator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dedo(
|
|
||||||
code: &str,
|
|
||||||
f: impl FnOnce(&mut v8::HandleScope, v8::Local<v8::Value>),
|
|
||||||
) {
|
|
||||||
v8_do(|| {
|
|
||||||
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);
|
|
||||||
let v = js_exec(scope, code);
|
|
||||||
|
|
||||||
f(scope, v);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dedo_json(code: &str, f: impl FnOnce(String)) {
|
|
||||||
let code = format!("JSON.stringify({})", code);
|
|
||||||
dedo(&code[..], |scope, v| {
|
|
||||||
let s: String = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
f(s);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn de_struct_v8(b: &mut Bencher) {
|
|
||||||
dedo("({arg1: 10, arg2: 123 })", |scope, obj| {
|
|
||||||
let mut total = 0;
|
|
||||||
b.iter(move || {
|
|
||||||
let op: MathOp = serde_v8::from_v8(scope, obj).unwrap();
|
|
||||||
total = total + op.arg1 + op.arg2;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn de_struct_v8_opt(b: &mut Bencher) {
|
|
||||||
dedo("({arg1: 10, arg2: 123 })", |scope, v| {
|
|
||||||
let k_arg1 = v8::String::new(scope, "arg1").unwrap().into();
|
|
||||||
let k_arg2 = v8::String::new(scope, "arg2").unwrap().into();
|
|
||||||
let obj = v8::Local::<v8::Object>::try_from(v).unwrap();
|
|
||||||
let mut total = 0;
|
|
||||||
b.iter(move || {
|
|
||||||
let v_arg1 = obj.get(scope, k_arg1).unwrap();
|
|
||||||
let v_arg2 = obj.get(scope, k_arg2).unwrap();
|
|
||||||
let op = MathOp {
|
|
||||||
arg1: serde_v8::from_v8(scope, v_arg1).unwrap(),
|
|
||||||
arg2: serde_v8::from_v8(scope, v_arg2).unwrap(),
|
|
||||||
operator: None,
|
|
||||||
};
|
|
||||||
total = total + op.arg1 + op.arg2;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn de_struct_json(b: &mut Bencher) {
|
|
||||||
dedo_json("({arg1: 10, arg2: 123 })", |s| {
|
|
||||||
let mut total = 0;
|
|
||||||
b.iter(move || {
|
|
||||||
let op: MathOp = serde_json::from_str(&s).unwrap();
|
|
||||||
total = total + op.arg1 + op.arg2;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn de_struct_json_deopt(b: &mut Bencher) {
|
|
||||||
// JSON.stringify() in loop (semi-simulating ABI loop)
|
|
||||||
dedo("({arg1: 10, arg2: 123 })", |scope, obj| {
|
|
||||||
let mut total = 0;
|
|
||||||
b.iter(move || {
|
|
||||||
let mut scope = v8::HandleScope::new(scope);
|
|
||||||
let s = v8::json::stringify(&mut scope, obj).unwrap();
|
|
||||||
let rs = s.to_rust_string_lossy(&mut scope);
|
|
||||||
let op: MathOp = serde_json::from_str(&rs).unwrap();
|
|
||||||
total = total + op.arg1 + op.arg2;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! dualbench {
|
|
||||||
($v8_fn:ident, $json_fn:ident, $src:expr, $t:ty) => {
|
|
||||||
fn $v8_fn(b: &mut Bencher) {
|
|
||||||
dedo($src, |scope, v| {
|
|
||||||
b.iter(move || {
|
|
||||||
let _: $t = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn $json_fn(b: &mut Bencher) {
|
|
||||||
dedo_json($src, |s| {
|
|
||||||
b.iter(move || {
|
|
||||||
let _: $t = serde_json::from_str(&s).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dualbench!(de_bool_v8, de_bool_json, "true", bool);
|
|
||||||
dualbench!(de_int_v8, de_int_json, "12345", u32);
|
|
||||||
dualbench!(
|
|
||||||
de_array_v8,
|
|
||||||
de_array_json,
|
|
||||||
"[1,2,3,4,5,6,7,8,9,10]",
|
|
||||||
Vec<u32>
|
|
||||||
);
|
|
||||||
dualbench!(de_str_v8, de_str_json, "'hello world'", String);
|
|
||||||
dualbench!(de_tuple_v8, de_tuple_json, "[1,false]", (u8, bool));
|
|
||||||
|
|
||||||
fn de_tuple_v8_opt(b: &mut Bencher) {
|
|
||||||
dedo("[1,false]", |scope, obj| {
|
|
||||||
let arr = v8::Local::<v8::Array>::try_from(obj).unwrap();
|
|
||||||
let obj = v8::Local::<v8::Object>::from(arr);
|
|
||||||
|
|
||||||
b.iter(move || {
|
|
||||||
let v1 = obj.get_index(scope, 0).unwrap();
|
|
||||||
let v2 = obj.get_index(scope, 1).unwrap();
|
|
||||||
let _: (u8, bool) = (
|
|
||||||
serde_v8::from_v8(scope, v1).unwrap(),
|
|
||||||
serde_v8::from_v8(scope, v2).unwrap(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark_group!(
|
|
||||||
benches,
|
|
||||||
de_struct_v8,
|
|
||||||
de_struct_v8_opt,
|
|
||||||
de_struct_json,
|
|
||||||
de_struct_json_deopt,
|
|
||||||
de_bool_v8,
|
|
||||||
de_bool_json,
|
|
||||||
de_int_v8,
|
|
||||||
de_int_json,
|
|
||||||
de_array_v8,
|
|
||||||
de_array_json,
|
|
||||||
de_str_v8,
|
|
||||||
de_str_json,
|
|
||||||
de_tuple_v8,
|
|
||||||
de_tuple_json,
|
|
||||||
de_tuple_v8_opt,
|
|
||||||
);
|
|
||||||
|
|
||||||
benchmark_main!(benches);
|
|
|
@ -1,108 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
|
||||||
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use serde_v8::utils::v8_do;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct MathOp {
|
|
||||||
arg1: u64,
|
|
||||||
arg2: u64,
|
|
||||||
operator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serdo(f: impl FnOnce(&mut v8::HandleScope)) {
|
|
||||||
v8_do(|| {
|
|
||||||
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);
|
|
||||||
|
|
||||||
f(scope);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! dualbench {
|
|
||||||
($v8_fn:ident, $json_fn:ident, $src:expr) => {
|
|
||||||
fn $v8_fn(b: &mut Bencher) {
|
|
||||||
serdo(|scope| {
|
|
||||||
let v = $src;
|
|
||||||
b.iter(move || {
|
|
||||||
let _ = serde_v8::to_v8(scope, &v).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn $json_fn(b: &mut Bencher) {
|
|
||||||
let v = $src;
|
|
||||||
b.iter(move || {
|
|
||||||
let _ = serde_json::to_string(&v).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dualbench!(
|
|
||||||
ser_struct_v8,
|
|
||||||
ser_struct_json,
|
|
||||||
MathOp {
|
|
||||||
arg1: 10,
|
|
||||||
arg2: 123,
|
|
||||||
operator: None
|
|
||||||
}
|
|
||||||
);
|
|
||||||
dualbench!(ser_bool_v8, ser_bool_json, true);
|
|
||||||
dualbench!(ser_int_v8, ser_int_json, 12345);
|
|
||||||
dualbench!(
|
|
||||||
ser_array_v8,
|
|
||||||
ser_array_json,
|
|
||||||
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
||||||
);
|
|
||||||
dualbench!(ser_str_v8, ser_str_json, "hello world");
|
|
||||||
dualbench!(ser_tuple_v8, ser_tuple_json, (1, false));
|
|
||||||
|
|
||||||
fn ser_struct_v8_manual(b: &mut Bencher) {
|
|
||||||
serdo(|scope| {
|
|
||||||
let v = MathOp {
|
|
||||||
arg1: 10,
|
|
||||||
arg2: 123,
|
|
||||||
operator: None,
|
|
||||||
};
|
|
||||||
b.iter(|| {
|
|
||||||
let obj = v8::Object::new(scope);
|
|
||||||
let k1 = v8::String::new(scope, "arg1").unwrap();
|
|
||||||
let k2 = v8::String::new(scope, "arg2").unwrap();
|
|
||||||
let k3 = v8::String::new(scope, "operator").unwrap();
|
|
||||||
// let k1 = v8::String::new_from_utf8(scope, "arg1".as_ref(), v8::NewStringType::Internalized).unwrap();
|
|
||||||
// let k2 = v8::String::new_from_utf8(scope, "arg2".as_ref(), v8::NewStringType::Internalized).unwrap();
|
|
||||||
// let k3 = v8::String::new_from_utf8(scope, "operator".as_ref(), v8::NewStringType::Internalized).unwrap();
|
|
||||||
let v1 = v8::Number::new(scope, v.arg1 as f64);
|
|
||||||
let v2 = v8::Number::new(scope, v.arg2 as f64);
|
|
||||||
let v3 = v8::null(scope);
|
|
||||||
obj.set(scope, k1.into(), v1.into()).unwrap();
|
|
||||||
obj.set(scope, k2.into(), v2.into()).unwrap();
|
|
||||||
obj.set(scope, k3.into(), v3.into()).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark_group!(
|
|
||||||
benches,
|
|
||||||
ser_struct_v8,
|
|
||||||
ser_struct_json,
|
|
||||||
ser_bool_v8,
|
|
||||||
ser_bool_json,
|
|
||||||
ser_int_v8,
|
|
||||||
ser_int_json,
|
|
||||||
ser_array_v8,
|
|
||||||
ser_array_json,
|
|
||||||
ser_str_v8,
|
|
||||||
ser_str_json,
|
|
||||||
ser_tuple_v8,
|
|
||||||
ser_tuple_json,
|
|
||||||
ser_struct_v8_manual,
|
|
||||||
);
|
|
||||||
benchmark_main!(benches);
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,617 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use serde::de::{self, Visitor};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
use crate::keys::{v8_struct_key, KeyCache};
|
|
||||||
use crate::payload::ValueType;
|
|
||||||
|
|
||||||
use crate::magic;
|
|
||||||
|
|
||||||
pub struct Deserializer<'a, 'b, 's> {
|
|
||||||
input: v8::Local<'a, v8::Value>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
_key_cache: Option<&'b mut KeyCache>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 's> Deserializer<'a, 'b, 's> {
|
|
||||||
pub fn new(
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
input: v8::Local<'a, v8::Value>,
|
|
||||||
key_cache: Option<&'b mut KeyCache>,
|
|
||||||
) -> Self {
|
|
||||||
Deserializer {
|
|
||||||
input,
|
|
||||||
scope,
|
|
||||||
_key_cache: key_cache,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// from_v8 deserializes a v8::Value into a Deserializable / rust struct
|
|
||||||
pub fn from_v8<'de, 'a, 'b, 's, T>(
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
input: v8::Local<'a, v8::Value>,
|
|
||||||
) -> Result<T>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
let mut deserializer = Deserializer::new(scope, input, None);
|
|
||||||
let t = T::deserialize(&mut deserializer)?;
|
|
||||||
Ok(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// like from_v8 except accepts a KeyCache to optimize struct key decoding
|
|
||||||
pub fn from_v8_cached<'de, 'a, 'b, 's, T>(
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
input: v8::Local<'a, v8::Value>,
|
|
||||||
key_cache: &mut KeyCache,
|
|
||||||
) -> Result<T>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
let mut deserializer = Deserializer::new(scope, input, Some(key_cache));
|
|
||||||
let t = T::deserialize(&mut deserializer)?;
|
|
||||||
Ok(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! wip {
|
|
||||||
($method:ident) => {
|
|
||||||
fn $method<V>(self, _v: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! deserialize_signed {
|
|
||||||
($dmethod:ident, $vmethod:ident, $t:tt) => {
|
|
||||||
fn $dmethod<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.$vmethod(self.input.integer_value(&mut self.scope).unwrap() as $t)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
|
||||||
for &'x mut Deserializer<'a, 'b, 's>
|
|
||||||
{
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
match ValueType::from_v8(self.input) {
|
|
||||||
ValueType::Null => self.deserialize_unit(visitor),
|
|
||||||
ValueType::Bool => self.deserialize_bool(visitor),
|
|
||||||
// Handle floats & ints separately to work with loosely-typed serde_json
|
|
||||||
ValueType::Number => {
|
|
||||||
if self.input.is_uint32() {
|
|
||||||
self.deserialize_u32(visitor)
|
|
||||||
} else if self.input.is_int32() {
|
|
||||||
self.deserialize_i32(visitor)
|
|
||||||
} else {
|
|
||||||
self.deserialize_f64(visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueType::String => self.deserialize_string(visitor),
|
|
||||||
ValueType::Array => self.deserialize_seq(visitor),
|
|
||||||
ValueType::Object => self.deserialize_map(visitor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
// Relaxed typechecking, will map all non-true vals to false
|
|
||||||
visitor.visit_bool(self.input.is_true())
|
|
||||||
}
|
|
||||||
|
|
||||||
deserialize_signed!(deserialize_i8, visit_i8, i8);
|
|
||||||
deserialize_signed!(deserialize_i16, visit_i16, i16);
|
|
||||||
deserialize_signed!(deserialize_i32, visit_i32, i32);
|
|
||||||
deserialize_signed!(deserialize_i64, visit_i64, i64);
|
|
||||||
// TODO: maybe handle unsigned by itself ?
|
|
||||||
deserialize_signed!(deserialize_u8, visit_u8, u8);
|
|
||||||
deserialize_signed!(deserialize_u16, visit_u16, u16);
|
|
||||||
deserialize_signed!(deserialize_u32, visit_u32, u32);
|
|
||||||
deserialize_signed!(deserialize_u64, visit_u64, u64);
|
|
||||||
|
|
||||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_f32(self.input.number_value(&mut self.scope).unwrap() as f32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_f64(self.input.number_value(&mut self.scope).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
wip!(deserialize_char);
|
|
||||||
|
|
||||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
self.deserialize_string(visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
if self.input.is_string() {
|
|
||||||
let v8_string = v8::Local::<v8::String>::try_from(self.input).unwrap();
|
|
||||||
let string = v8_string.to_rust_string_lossy(self.scope);
|
|
||||||
visitor.visit_string(string)
|
|
||||||
} else {
|
|
||||||
Err(Error::ExpectedString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wip!(deserialize_bytes);
|
|
||||||
wip!(deserialize_byte_buf);
|
|
||||||
|
|
||||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
if self.input.is_null_or_undefined() {
|
|
||||||
visitor.visit_none()
|
|
||||||
} else {
|
|
||||||
visitor.visit_some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
if self.input.is_null_or_undefined() {
|
|
||||||
visitor.visit_unit()
|
|
||||||
} else {
|
|
||||||
Err(Error::ExpectedNull)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_unit_struct<V>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
self.deserialize_unit(visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// As is done here, serializers are encouraged to treat newtype structs as
|
|
||||||
// insignificant wrappers around the data they contain. That means not
|
|
||||||
// parsing anything other than the contained value.
|
|
||||||
fn deserialize_newtype_struct<V>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_newtype_struct(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
let arr = v8::Local::<v8::Array>::try_from(self.input)
|
|
||||||
.map_err(|_| Error::ExpectedArray)?;
|
|
||||||
let len = arr.length();
|
|
||||||
let obj = v8::Local::<v8::Object>::from(arr);
|
|
||||||
let seq = SeqAccess {
|
|
||||||
pos: 0,
|
|
||||||
len,
|
|
||||||
obj,
|
|
||||||
scope: self.scope,
|
|
||||||
};
|
|
||||||
visitor.visit_seq(seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like deserialize_seq except it prefers tuple's length over input array's length
|
|
||||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
// TODO: error on length mismatch
|
|
||||||
let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap();
|
|
||||||
let seq = SeqAccess {
|
|
||||||
pos: 0,
|
|
||||||
len: len as u32,
|
|
||||||
obj,
|
|
||||||
scope: self.scope,
|
|
||||||
};
|
|
||||||
visitor.visit_seq(seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tuple structs look just like sequences in JSON.
|
|
||||||
fn deserialize_tuple_struct<V>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
len: usize,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
self.deserialize_tuple(len, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: de::Visitor<'de>,
|
|
||||||
{
|
|
||||||
// Assume object, then get_own_property_names
|
|
||||||
let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap();
|
|
||||||
let prop_names = obj.get_own_property_names(self.scope);
|
|
||||||
let mut keys: Vec<magic::Value> = match prop_names {
|
|
||||||
Some(names) => from_v8(self.scope, names.into()).unwrap(),
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
let keys: Vec<v8::Local<v8::Value>> = keys
|
|
||||||
.drain(..)
|
|
||||||
.map(|x| x.into())
|
|
||||||
// Filter keys to drop keys whose value is undefined
|
|
||||||
// TODO: optimize, since this doubles our get calls
|
|
||||||
.filter(|key| !obj.get(self.scope, *key).unwrap().is_undefined())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let map = MapAccess {
|
|
||||||
obj,
|
|
||||||
keys,
|
|
||||||
pos: 0,
|
|
||||||
scope: self.scope,
|
|
||||||
};
|
|
||||||
visitor.visit_map(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_struct<V>(
|
|
||||||
self,
|
|
||||||
name: &'static str,
|
|
||||||
fields: &'static [&'static str],
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
// Magic for serde_v8::magic::Value, to passthrough v8::Value
|
|
||||||
// TODO: ensure this is cross-platform and there's no alternative
|
|
||||||
if name == magic::NAME {
|
|
||||||
let mv = magic::Value {
|
|
||||||
v8_value: self.input,
|
|
||||||
};
|
|
||||||
let hack: u64 = unsafe { std::mem::transmute(mv) };
|
|
||||||
return visitor.visit_u64(hack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Magic Buffer
|
|
||||||
if name == magic::buffer::BUF_NAME {
|
|
||||||
let zero_copy_buf =
|
|
||||||
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
|
||||||
.map(|view| magic::zero_copy_buf::ZeroCopyBuf::new(self.scope, view))
|
|
||||||
.map_err(|_| Error::ExpectedArray)?;
|
|
||||||
let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) };
|
|
||||||
return visitor.visit_bytes(&data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular struct
|
|
||||||
let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap();
|
|
||||||
let map = ObjectAccess {
|
|
||||||
fields,
|
|
||||||
obj,
|
|
||||||
pos: 0,
|
|
||||||
scope: self.scope,
|
|
||||||
_cache: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
visitor.visit_map(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// To be compatible with `serde-json`, we expect enums to be:
|
|
||||||
/// - `"Variant"`: strings for unit variants, i.e: Enum::Variant
|
|
||||||
/// - `{ Variant: payload }`: single K/V pairs, converted to `Enum::Variant { payload }`
|
|
||||||
fn deserialize_enum<V>(
|
|
||||||
self,
|
|
||||||
_name: &str,
|
|
||||||
_variants: &'static [&'static str],
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
// Unit variant
|
|
||||||
if self.input.is_string() {
|
|
||||||
let payload = v8::undefined(self.scope).into();
|
|
||||||
visitor.visit_enum(EnumAccess {
|
|
||||||
scope: self.scope,
|
|
||||||
tag: self.input,
|
|
||||||
payload,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Struct or tuple variant
|
|
||||||
else if self.input.is_object() {
|
|
||||||
// Assume object
|
|
||||||
let obj = v8::Local::<v8::Object>::try_from(self.input).unwrap();
|
|
||||||
// Unpack single-key
|
|
||||||
let tag = {
|
|
||||||
let prop_names = obj.get_own_property_names(self.scope);
|
|
||||||
let prop_names = prop_names.ok_or(Error::ExpectedEnum)?;
|
|
||||||
if prop_names.length() != 1 {
|
|
||||||
return Err(Error::LengthMismatch);
|
|
||||||
}
|
|
||||||
prop_names.get_index(self.scope, 0).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let payload = obj.get(self.scope, tag).unwrap();
|
|
||||||
visitor.visit_enum(EnumAccess {
|
|
||||||
scope: self.scope,
|
|
||||||
tag,
|
|
||||||
payload,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// TODO: improve error
|
|
||||||
Err(Error::ExpectedEnum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An identifier in Serde is the type that identifies a field of a struct or
|
|
||||||
// the variant of an enum. In JSON, struct fields and enum variants are
|
|
||||||
// represented as strings. In other formats they may be represented as
|
|
||||||
// numeric indices.
|
|
||||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
self.deserialize_str(visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: Visitor<'de>,
|
|
||||||
{
|
|
||||||
visitor.visit_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MapAccess<'a, 'b, 's> {
|
|
||||||
obj: v8::Local<'a, v8::Object>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
keys: Vec<v8::Local<'a, v8::Value>>,
|
|
||||||
pos: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> de::MapAccess<'de> for MapAccess<'_, '_, '_> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn next_key_seed<K: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: K,
|
|
||||||
) -> Result<Option<K::Value>> {
|
|
||||||
Ok(match self.keys.get(self.pos) {
|
|
||||||
Some(key) => {
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, *key, None);
|
|
||||||
Some(seed.deserialize(&mut deserializer)?)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_value_seed<V: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: V,
|
|
||||||
) -> Result<V::Value> {
|
|
||||||
if self.pos >= self.keys.len() {
|
|
||||||
return Err(Error::LengthMismatch);
|
|
||||||
}
|
|
||||||
let key = self.keys[self.pos];
|
|
||||||
self.pos += 1;
|
|
||||||
let v8_val = self.obj.get(self.scope, key).unwrap();
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, v8_val, None);
|
|
||||||
seed.deserialize(&mut deserializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_entry_seed<
|
|
||||||
K: de::DeserializeSeed<'de>,
|
|
||||||
V: de::DeserializeSeed<'de>,
|
|
||||||
>(
|
|
||||||
&mut self,
|
|
||||||
kseed: K,
|
|
||||||
vseed: V,
|
|
||||||
) -> Result<Option<(K::Value, V::Value)>> {
|
|
||||||
if self.pos >= self.keys.len() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let v8_key = self.keys[self.pos];
|
|
||||||
self.pos += 1;
|
|
||||||
let mut kdeserializer = Deserializer::new(self.scope, v8_key, None);
|
|
||||||
Ok(Some((kseed.deserialize(&mut kdeserializer)?, {
|
|
||||||
let v8_val = self.obj.get(self.scope, v8_key).unwrap();
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, v8_val, None);
|
|
||||||
vseed.deserialize(&mut deserializer)?
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ObjectAccess<'a, 'b, 's> {
|
|
||||||
obj: v8::Local<'a, v8::Object>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
fields: &'static [&'static str],
|
|
||||||
pos: usize,
|
|
||||||
_cache: Option<&'b mut KeyCache>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn str_deserializer(s: &str) -> de::value::StrDeserializer<Error> {
|
|
||||||
de::IntoDeserializer::into_deserializer(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, 'b, 's> de::MapAccess<'de> for ObjectAccess<'a, 'b, 's> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn next_key_seed<K: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: K,
|
|
||||||
) -> Result<Option<K::Value>> {
|
|
||||||
Ok(match self.fields.get(self.pos) {
|
|
||||||
Some(&field) => Some(seed.deserialize(str_deserializer(field))?),
|
|
||||||
None => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_value_seed<V: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: V,
|
|
||||||
) -> Result<V::Value> {
|
|
||||||
if self.pos >= self.fields.len() {
|
|
||||||
return Err(Error::LengthMismatch);
|
|
||||||
}
|
|
||||||
let field = self.fields[self.pos];
|
|
||||||
self.pos += 1;
|
|
||||||
let key = v8_struct_key(self.scope, field).into();
|
|
||||||
let v8_val = self.obj.get(self.scope, key).unwrap();
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, v8_val, None);
|
|
||||||
seed.deserialize(&mut deserializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_entry_seed<
|
|
||||||
K: de::DeserializeSeed<'de>,
|
|
||||||
V: de::DeserializeSeed<'de>,
|
|
||||||
>(
|
|
||||||
&mut self,
|
|
||||||
kseed: K,
|
|
||||||
vseed: V,
|
|
||||||
) -> Result<Option<(K::Value, V::Value)>> {
|
|
||||||
if self.pos >= self.fields.len() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let field = self.fields[self.pos];
|
|
||||||
self.pos += 1;
|
|
||||||
Ok(Some((kseed.deserialize(str_deserializer(field))?, {
|
|
||||||
let key = v8_struct_key(self.scope, field).into();
|
|
||||||
let v8_val = self.obj.get(self.scope, key).unwrap();
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, v8_val, None);
|
|
||||||
vseed.deserialize(&mut deserializer)?
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SeqAccess<'a, 'b, 's> {
|
|
||||||
obj: v8::Local<'a, v8::Object>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
len: u32,
|
|
||||||
pos: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> de::SeqAccess<'de> for SeqAccess<'_, '_, '_> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn next_element_seed<T: de::DeserializeSeed<'de>>(
|
|
||||||
&mut self,
|
|
||||||
seed: T,
|
|
||||||
) -> Result<Option<T::Value>> {
|
|
||||||
let pos = self.pos;
|
|
||||||
self.pos += 1;
|
|
||||||
|
|
||||||
if pos < self.len {
|
|
||||||
let val = self.obj.get_index(self.scope, pos).unwrap();
|
|
||||||
let mut deserializer = Deserializer::new(self.scope, val, None);
|
|
||||||
Ok(Some(seed.deserialize(&mut deserializer)?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> Option<usize> {
|
|
||||||
Some((self.len - self.pos) as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EnumAccess<'a, 'b, 's> {
|
|
||||||
tag: v8::Local<'a, v8::Value>,
|
|
||||||
payload: v8::Local<'a, v8::Value>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
// p1: std::marker::PhantomData<&'x ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, 'b, 's, 'x> de::EnumAccess<'de> for EnumAccess<'a, 'b, 's> {
|
|
||||||
type Error = Error;
|
|
||||||
type Variant = VariantDeserializer<'a, 'b, 's>;
|
|
||||||
|
|
||||||
fn variant_seed<V: de::DeserializeSeed<'de>>(
|
|
||||||
self,
|
|
||||||
seed: V,
|
|
||||||
) -> Result<(V::Value, Self::Variant)> {
|
|
||||||
let seed = {
|
|
||||||
let mut dtag = Deserializer::new(self.scope, self.tag, None);
|
|
||||||
seed.deserialize(&mut dtag)
|
|
||||||
};
|
|
||||||
let dpayload = VariantDeserializer::<'a, 'b, 's> {
|
|
||||||
scope: self.scope,
|
|
||||||
value: self.payload,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((seed?, dpayload))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VariantDeserializer<'a, 'b, 's> {
|
|
||||||
value: v8::Local<'a, v8::Value>,
|
|
||||||
scope: &'b mut v8::HandleScope<'s>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a, 'b, 's> de::VariantAccess<'de>
|
|
||||||
for VariantDeserializer<'a, 'b, 's>
|
|
||||||
{
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn unit_variant(self) -> Result<()> {
|
|
||||||
let mut d = Deserializer::new(self.scope, self.value, None);
|
|
||||||
de::Deserialize::deserialize(&mut d)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn newtype_variant_seed<T: de::DeserializeSeed<'de>>(
|
|
||||||
self,
|
|
||||||
seed: T,
|
|
||||||
) -> Result<T::Value> {
|
|
||||||
let mut d = Deserializer::new(self.scope, self.value, None);
|
|
||||||
seed.deserialize(&mut d)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_variant<V: de::Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
len: usize,
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value> {
|
|
||||||
let mut d = Deserializer::new(self.scope, self.value, None);
|
|
||||||
de::Deserializer::deserialize_tuple(&mut d, len, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn struct_variant<V: de::Visitor<'de>>(
|
|
||||||
self,
|
|
||||||
fields: &'static [&'static str],
|
|
||||||
visitor: V,
|
|
||||||
) -> Result<V::Value> {
|
|
||||||
let mut d = Deserializer::new(self.scope, self.value, None);
|
|
||||||
de::Deserializer::deserialize_struct(&mut d, "", fields, visitor)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
|
|
||||||
use serde::{de, ser};
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
Message(String),
|
|
||||||
|
|
||||||
ExpectedBoolean,
|
|
||||||
ExpectedInteger,
|
|
||||||
ExpectedString,
|
|
||||||
ExpectedNull,
|
|
||||||
ExpectedArray,
|
|
||||||
ExpectedMap,
|
|
||||||
ExpectedEnum,
|
|
||||||
|
|
||||||
ExpectedUtf8,
|
|
||||||
|
|
||||||
LengthMismatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ser::Error for Error {
|
|
||||||
fn custom<T: Display>(msg: T) -> Self {
|
|
||||||
Error::Message(msg.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl de::Error for Error {
|
|
||||||
fn custom<T: Display>(msg: T) -> Self {
|
|
||||||
Error::Message(msg.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::Message(msg) => formatter.write_str(msg),
|
|
||||||
err => formatter.write_str(format!("serde_v8 error: {:?}", err).as_ref()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
// KeyCache stores a pool struct keys mapped to v8,
|
|
||||||
// to minimize allocs and speed up decoding/encoding `v8::Object`s
|
|
||||||
// TODO: experiment with in from_v8/to_v8
|
|
||||||
pub struct KeyCache(HashMap<&'static str, v8::Global<v8::String>>);
|
|
||||||
|
|
||||||
// creates an optimized v8::String for a struct field
|
|
||||||
// TODO: experiment with external strings
|
|
||||||
// TODO: evaluate if own KeyCache is better than v8's dedupe
|
|
||||||
pub fn v8_struct_key<'s>(
|
|
||||||
scope: &mut v8::HandleScope<'s>,
|
|
||||||
field: &'static str,
|
|
||||||
) -> v8::Local<'s, v8::String> {
|
|
||||||
// Internalized v8 strings are significantly faster than "normal" v8 strings
|
|
||||||
// since v8 deduplicates re-used strings minimizing new allocations
|
|
||||||
// see: https://github.com/v8/v8/blob/14ac92e02cc3db38131a57e75e2392529f405f2f/include/v8.h#L3165-L3171
|
|
||||||
v8::String::new_from_utf8(
|
|
||||||
scope,
|
|
||||||
field.as_ref(),
|
|
||||||
v8::NewStringType::Internalized,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
|
|
||||||
// TODO: consider external strings later
|
|
||||||
// right now non-deduped external strings (without KeyCache)
|
|
||||||
// are slower than the deduped internalized strings by ~2.5x
|
|
||||||
// since they're a new string in v8's eyes and needs to be hashed, etc...
|
|
||||||
// v8::String::new_external_onebyte_static(scope, field).unwrap()
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
mod de;
|
|
||||||
mod error;
|
|
||||||
mod keys;
|
|
||||||
mod magic;
|
|
||||||
mod payload;
|
|
||||||
mod ser;
|
|
||||||
mod serializable;
|
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
pub use de::{from_v8, from_v8_cached, Deserializer};
|
|
||||||
pub use error::{Error, Result};
|
|
||||||
pub use keys::KeyCache;
|
|
||||||
pub use magic::buffer::MagicBuffer as Buffer;
|
|
||||||
pub use magic::Value;
|
|
||||||
pub use ser::{to_v8, Serializer};
|
|
||||||
pub use serializable::{Serializable, SerializablePkg};
|
|
|
@ -1,139 +0,0 @@
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use super::zero_copy_buf::ZeroCopyBuf;
|
|
||||||
|
|
||||||
// An asymmetric wrapper around ZeroCopyBuf,
|
|
||||||
// allowing us to use a single type for familiarity
|
|
||||||
pub enum MagicBuffer {
|
|
||||||
FromV8(ZeroCopyBuf),
|
|
||||||
ToV8(Mutex<Option<Box<[u8]>>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MagicBuffer {
|
|
||||||
pub fn new<'s>(
|
|
||||||
scope: &mut v8::HandleScope<'s>,
|
|
||||||
view: v8::Local<v8::ArrayBufferView>,
|
|
||||||
) -> Self {
|
|
||||||
Self::FromV8(ZeroCopyBuf::new(scope, view))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for MagicBuffer {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::FromV8(zbuf) => Self::FromV8(zbuf.clone()),
|
|
||||||
Self::ToV8(_) => panic!("Don't Clone a MagicBuffer sent to v8"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for MagicBuffer {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[u8]> for MagicBuffer {
|
|
||||||
fn as_mut(&mut self) -> &mut [u8] {
|
|
||||||
&mut *self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for MagicBuffer {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
match self {
|
|
||||||
Self::FromV8(buf) => &*buf,
|
|
||||||
Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for MagicBuffer {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
match self {
|
|
||||||
Self::FromV8(buf) => &mut *buf,
|
|
||||||
Self::ToV8(_) => panic!("Don't Deref a MagicBuffer sent to v8"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<[u8]>> for MagicBuffer {
|
|
||||||
fn from(buf: Box<[u8]>) -> Self {
|
|
||||||
MagicBuffer::ToV8(Mutex::new(Some(buf)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for MagicBuffer {
|
|
||||||
fn from(vec: Vec<u8>) -> Self {
|
|
||||||
vec.into_boxed_slice().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const BUF_NAME: &str = "$__v8_magic_Buffer";
|
|
||||||
pub const BUF_FIELD_1: &str = "$__v8_magic_buffer_1";
|
|
||||||
pub const BUF_FIELD_2: &str = "$__v8_magic_buffer_2";
|
|
||||||
|
|
||||||
impl serde::Serialize for MagicBuffer {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
|
|
||||||
let mut s = serializer.serialize_struct(BUF_NAME, 1)?;
|
|
||||||
let boxed: Box<[u8]> = match self {
|
|
||||||
Self::FromV8(buf) => {
|
|
||||||
let value: &[u8] = &buf;
|
|
||||||
value.into()
|
|
||||||
}
|
|
||||||
Self::ToV8(x) => x.lock().unwrap().take().expect("MagicBuffer was empty"),
|
|
||||||
};
|
|
||||||
let hack: [usize; 2] = unsafe { std::mem::transmute(boxed) };
|
|
||||||
let f1: u64 = hack[0] as u64;
|
|
||||||
let f2: u64 = hack[1] as u64;
|
|
||||||
s.serialize_field(BUF_FIELD_1, &f1)?;
|
|
||||||
s.serialize_field(BUF_FIELD_2, &f2)?;
|
|
||||||
s.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 's> serde::Deserialize<'de> for MagicBuffer {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<MagicBuffer, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct ValueVisitor {}
|
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for ValueVisitor {
|
|
||||||
type Value = MagicBuffer;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
formatter.write_str("a serde_v8::MagicBuffer")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
let p1: &[usize] = unsafe { &*(v as *const [u8] as *const [usize]) };
|
|
||||||
let p2: [usize; 4] = [p1[0], p1[1], p1[2], p1[3]];
|
|
||||||
let zero_copy: ZeroCopyBuf = unsafe { std::mem::transmute(p2) };
|
|
||||||
Ok(MagicBuffer::FromV8(zero_copy))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FIELDS: [&str; 0] = [];
|
|
||||||
let visitor = ValueVisitor {};
|
|
||||||
deserializer.deserialize_struct(BUF_NAME, &FIELDS, visitor)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
use serde::ser::{Impossible, Serialize, Serializer};
|
|
||||||
|
|
||||||
/// All serde_v8 "magic" values are reduced to structs with 1 or 2 u64 fields
|
|
||||||
/// assuming usize==u64, most types are simply a pointer or pointer+len (e.g: Box<T>)
|
|
||||||
pub type TransmutedField = u64;
|
|
||||||
pub type FieldResult = Result<TransmutedField>;
|
|
||||||
|
|
||||||
macro_rules! not_reachable {
|
|
||||||
($($name:ident($ty:ty);)*) => {
|
|
||||||
$(fn $name(self, _v: $ty) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FieldSerializer is a simple serde::Serializer that only returns u64s
|
|
||||||
/// it allows the "magic" struct serializers to obtain the transmuted field values
|
|
||||||
pub struct FieldSerializer {}
|
|
||||||
|
|
||||||
impl Serializer for FieldSerializer {
|
|
||||||
type Ok = TransmutedField;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
type SerializeSeq = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeTuple = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeTupleStruct = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeTupleVariant = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeMap = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeStruct = Impossible<TransmutedField, Error>;
|
|
||||||
type SerializeStructVariant = Impossible<TransmutedField, Error>;
|
|
||||||
|
|
||||||
fn serialize_u64(self, transmuted_field: u64) -> FieldResult {
|
|
||||||
Ok(transmuted_field)
|
|
||||||
}
|
|
||||||
|
|
||||||
not_reachable! {
|
|
||||||
serialize_i8(i8);
|
|
||||||
serialize_i16(i16);
|
|
||||||
serialize_i32(i32);
|
|
||||||
serialize_i64(i64);
|
|
||||||
serialize_u8(u8);
|
|
||||||
serialize_u16(u16);
|
|
||||||
serialize_u32(u32);
|
|
||||||
// serialize_u64(TransmutedField); the chosen one
|
|
||||||
serialize_f32(f32);
|
|
||||||
serialize_f64(f64);
|
|
||||||
serialize_bool(bool);
|
|
||||||
serialize_char(char);
|
|
||||||
serialize_str(&str);
|
|
||||||
serialize_bytes(&[u8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_none(self) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit(self) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_value: &T,
|
|
||||||
) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_value: &T,
|
|
||||||
) -> FieldResult {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleStruct> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleVariant> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStruct> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_struct_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
_variant: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStructVariant> {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
pub mod buffer;
|
|
||||||
mod field;
|
|
||||||
mod value;
|
|
||||||
pub mod zero_copy_buf;
|
|
||||||
|
|
||||||
pub use field::FieldSerializer;
|
|
||||||
pub use value::{Value, FIELD, NAME};
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
pub const FIELD: &str = "$__v8_magic_value";
|
|
||||||
pub const NAME: &str = "$__v8_magic_Value";
|
|
||||||
|
|
||||||
/// serde_v8::Value allows passing through `v8::Value`s untouched
|
|
||||||
/// when encoding/decoding and allows mixing rust & v8 values in
|
|
||||||
/// structs, tuples...
|
|
||||||
/// The implementation mainly breaks down to:
|
|
||||||
/// 1. Transmuting between u64 <> serde_v8::Value
|
|
||||||
/// 2. Using special struct/field names to detect these values
|
|
||||||
/// 3. Then serde "boilerplate"
|
|
||||||
pub struct Value<'s> {
|
|
||||||
pub v8_value: v8::Local<'s, v8::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> From<v8::Local<'s, v8::Value>> for Value<'s> {
|
|
||||||
fn from(v8_value: v8::Local<'s, v8::Value>) -> Self {
|
|
||||||
Self { v8_value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> From<Value<'s>> for v8::Local<'s, v8::Value> {
|
|
||||||
fn from(v: Value<'s>) -> Self {
|
|
||||||
v.v8_value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl serde::Serialize for Value<'_> {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
|
|
||||||
let mut s = serializer.serialize_struct(NAME, 1)?;
|
|
||||||
let mv = Value {
|
|
||||||
v8_value: self.v8_value,
|
|
||||||
};
|
|
||||||
let hack: u64 = unsafe { std::mem::transmute(mv) };
|
|
||||||
s.serialize_field(FIELD, &hack)?;
|
|
||||||
s.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 's> serde::Deserialize<'de> for Value<'s> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Value<'s>, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct ValueVisitor<'s> {
|
|
||||||
p1: PhantomData<&'s ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 's> serde::de::Visitor<'de> for ValueVisitor<'s> {
|
|
||||||
type Value = Value<'s>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
formatter.write_str("a v8::Value")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
let mv: Value<'s> = unsafe { std::mem::transmute(v) };
|
|
||||||
Ok(mv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FIELDS: [&str; 1] = [FIELD];
|
|
||||||
let visitor = ValueVisitor { p1: PhantomData };
|
|
||||||
deserializer.deserialize_struct(NAME, &FIELDS, visitor)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
|
|
||||||
/// A ZeroCopyBuf encapsulates a slice that's been borrowed from a JavaScript
|
|
||||||
/// ArrayBuffer object. JavaScript objects can normally be garbage collected,
|
|
||||||
/// but the existence of a ZeroCopyBuf inhibits this until it is dropped. It
|
|
||||||
/// behaves much like an Arc<[u8]>.
|
|
||||||
///
|
|
||||||
/// # Cloning
|
|
||||||
/// Cloning a ZeroCopyBuf does not clone the contents of the buffer,
|
|
||||||
/// it creates a new reference to that buffer.
|
|
||||||
///
|
|
||||||
/// To actually clone the contents of the buffer do
|
|
||||||
/// `let copy = Vec::from(&*zero_copy_buf);`
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ZeroCopyBuf {
|
|
||||||
backing_store: v8::SharedRef<v8::BackingStore>,
|
|
||||||
byte_offset: usize,
|
|
||||||
byte_length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for ZeroCopyBuf {}
|
|
||||||
|
|
||||||
impl ZeroCopyBuf {
|
|
||||||
pub fn new<'s>(
|
|
||||||
scope: &mut v8::HandleScope<'s>,
|
|
||||||
view: v8::Local<v8::ArrayBufferView>,
|
|
||||||
) -> Self {
|
|
||||||
let backing_store = view.buffer(scope).unwrap().get_backing_store();
|
|
||||||
let byte_offset = view.byte_offset();
|
|
||||||
let byte_length = view.byte_length();
|
|
||||||
Self {
|
|
||||||
backing_store,
|
|
||||||
byte_offset,
|
|
||||||
byte_length,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ZeroCopyBuf {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
get_backing_store_slice(
|
|
||||||
&self.backing_store,
|
|
||||||
self.byte_offset,
|
|
||||||
self.byte_length,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ZeroCopyBuf {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
get_backing_store_slice_mut(
|
|
||||||
&self.backing_store,
|
|
||||||
self.byte_offset,
|
|
||||||
self.byte_length,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for ZeroCopyBuf {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[u8]> for ZeroCopyBuf {
|
|
||||||
fn as_mut(&mut self) -> &mut [u8] {
|
|
||||||
&mut *self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_backing_store_slice(
|
|
||||||
backing_store: &v8::SharedRef<v8::BackingStore>,
|
|
||||||
byte_offset: usize,
|
|
||||||
byte_length: usize,
|
|
||||||
) -> &[u8] {
|
|
||||||
let cells: *const [Cell<u8>] =
|
|
||||||
&backing_store[byte_offset..byte_offset + byte_length];
|
|
||||||
let bytes = cells as *const [u8];
|
|
||||||
&*bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
|
||||||
unsafe fn get_backing_store_slice_mut(
|
|
||||||
backing_store: &v8::SharedRef<v8::BackingStore>,
|
|
||||||
byte_offset: usize,
|
|
||||||
byte_length: usize,
|
|
||||||
) -> &mut [u8] {
|
|
||||||
let cells: *const [Cell<u8>] =
|
|
||||||
&backing_store[byte_offset..byte_offset + byte_length];
|
|
||||||
let bytes = cells as *const _ as *mut [u8];
|
|
||||||
&mut *bytes
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
// TODO: maybe add a Payload type that holds scope & v8::Value
|
|
||||||
// so it can implement Deserialize by itself
|
|
||||||
|
|
||||||
// Classifies v8::Values into sub-types
|
|
||||||
pub enum ValueType {
|
|
||||||
Null,
|
|
||||||
Bool,
|
|
||||||
Number,
|
|
||||||
String,
|
|
||||||
Array,
|
|
||||||
Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueType {
|
|
||||||
pub fn from_v8(v: v8::Local<v8::Value>) -> ValueType {
|
|
||||||
if v.is_boolean() {
|
|
||||||
return Self::Bool;
|
|
||||||
} else if v.is_number() {
|
|
||||||
return Self::Number;
|
|
||||||
} else if v.is_string() {
|
|
||||||
return Self::String;
|
|
||||||
} else if v.is_array() {
|
|
||||||
return Self::Array;
|
|
||||||
} else if v.is_object() {
|
|
||||||
return Self::Object;
|
|
||||||
} else if v.is_null_or_undefined() {
|
|
||||||
return Self::Null;
|
|
||||||
}
|
|
||||||
panic!("serde_v8: unknown ValueType for v8::Value")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,559 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use serde::ser;
|
|
||||||
use serde::ser::Serialize;
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
use crate::keys::v8_struct_key;
|
|
||||||
use crate::magic;
|
|
||||||
|
|
||||||
type JsValue<'s> = v8::Local<'s, v8::Value>;
|
|
||||||
type JsResult<'s> = Result<JsValue<'s>>;
|
|
||||||
|
|
||||||
type ScopePtr<'a, 'b, 'c> = &'c RefCell<&'b mut v8::HandleScope<'a>>;
|
|
||||||
|
|
||||||
pub fn to_v8<'a, T>(scope: &mut v8::HandleScope<'a>, input: T) -> JsResult<'a>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
let scopeptr = RefCell::new(scope);
|
|
||||||
let serializer = Serializer::new(&scopeptr);
|
|
||||||
|
|
||||||
input.serialize(serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps other serializers into an enum tagged variant form.
|
|
||||||
/// Uses {"Variant": ...payload...} for compatibility with serde-json.
|
|
||||||
pub struct VariantSerializer<'a, 'b, 'c, S> {
|
|
||||||
inner: S,
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
variant: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c, S> VariantSerializer<'a, 'b, 'c, S> {
|
|
||||||
pub fn new(
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
variant: &'static str,
|
|
||||||
inner: S,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
scope,
|
|
||||||
variant,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self, inner: impl FnOnce(S) -> JsResult<'a>) -> JsResult<'a> {
|
|
||||||
let value = inner(self.inner)?;
|
|
||||||
let scope = &mut *self.scope.borrow_mut();
|
|
||||||
let obj = v8::Object::new(scope);
|
|
||||||
let key = v8_struct_key(scope, self.variant).into();
|
|
||||||
obj.set(scope, key, value);
|
|
||||||
Ok(obj.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c, S> ser::SerializeTupleVariant
|
|
||||||
for VariantSerializer<'a, 'b, 'c, S>
|
|
||||||
where
|
|
||||||
S: ser::SerializeTupleStruct<Ok = JsValue<'a>, Error = Error>,
|
|
||||||
{
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.inner.serialize_field(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
self.end(S::end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c, S> ser::SerializeStructVariant
|
|
||||||
for VariantSerializer<'a, 'b, 'c, S>
|
|
||||||
where
|
|
||||||
S: ser::SerializeStruct<Ok = JsValue<'a>, Error = Error>,
|
|
||||||
{
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.inner.serialize_field(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
self.end(S::end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ArraySerializer<'a, 'b, 'c> {
|
|
||||||
pending: Vec<JsValue<'a>>,
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> {
|
|
||||||
pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option<usize>) -> Self {
|
|
||||||
let pending = match len {
|
|
||||||
Some(len) => Vec::with_capacity(len),
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
Self { pending, scope }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeSeq for ArraySerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_element<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
let x = value.serialize(Serializer::new(self.scope))?;
|
|
||||||
self.pending.push(x);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
let elements = self.pending.iter().as_slice();
|
|
||||||
let scope = &mut *self.scope.borrow_mut();
|
|
||||||
let arr = v8::Array::new_with_elements(scope, elements);
|
|
||||||
Ok(arr.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeTuple for ArraySerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_element<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
ser::SerializeSeq::serialize_element(self, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
ser::SerializeSeq::end(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeTupleStruct for ArraySerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
ser::SerializeTuple::serialize_element(self, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
ser::SerializeTuple::end(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ObjectSerializer<'a, 'b, 'c> {
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
obj: v8::Local<'a, v8::Object>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ObjectSerializer<'a, 'b, 'c> {
|
|
||||||
pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self {
|
|
||||||
let obj = v8::Object::new(&mut *scope.borrow_mut());
|
|
||||||
Self { scope, obj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeStruct for ObjectSerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
let value = value.serialize(Serializer::new(self.scope))?;
|
|
||||||
let scope = &mut *self.scope.borrow_mut();
|
|
||||||
let key = v8_struct_key(scope, key).into();
|
|
||||||
self.obj.set(scope, key, value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
Ok(self.obj.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MagicSerializer<'a> {
|
|
||||||
v8_value: Option<v8::Local<'a, v8::Value>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ser::SerializeStruct for MagicSerializer<'a> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
if key != magic::FIELD {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
let transmuted: u64 = value.serialize(magic::FieldSerializer {})?;
|
|
||||||
let mv: magic::Value<'a> = unsafe { std::mem::transmute(transmuted) };
|
|
||||||
self.v8_value = Some(mv.v8_value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
Ok(self.v8_value.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(@AaronO): refactor this and streamline how we transmute values
|
|
||||||
pub struct MagicBufferSerializer<'a, 'b, 'c> {
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
f1: u64,
|
|
||||||
f2: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> MagicBufferSerializer<'a, 'b, 'c> {
|
|
||||||
pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self {
|
|
||||||
Self {
|
|
||||||
scope,
|
|
||||||
f1: 0,
|
|
||||||
f2: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeStruct for MagicBufferSerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
// Get u64 chunk
|
|
||||||
let transmuted: u64 = value.serialize(magic::FieldSerializer {})?;
|
|
||||||
match key {
|
|
||||||
magic::buffer::BUF_FIELD_1 => self.f1 = transmuted,
|
|
||||||
magic::buffer::BUF_FIELD_2 => self.f2 = transmuted,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
let x: [usize; 2] = [self.f1 as usize, self.f2 as usize];
|
|
||||||
let buf: Box<[u8]> = unsafe { std::mem::transmute(x) };
|
|
||||||
let scope = &mut *self.scope.borrow_mut();
|
|
||||||
let v8_value = boxed_slice_to_uint8array(scope, buf);
|
|
||||||
Ok(v8_value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatches between magic and regular struct serializers
|
|
||||||
pub enum StructSerializers<'a, 'b, 'c> {
|
|
||||||
Magic(MagicSerializer<'a>),
|
|
||||||
MagicBuffer(MagicBufferSerializer<'a, 'b, 'c>),
|
|
||||||
Regular(ObjectSerializer<'a, 'b, 'c>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
key: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
StructSerializers::Magic(s) => s.serialize_field(key, value),
|
|
||||||
StructSerializers::MagicBuffer(s) => s.serialize_field(key, value),
|
|
||||||
StructSerializers::Regular(s) => s.serialize_field(key, value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
match self {
|
|
||||||
StructSerializers::Magic(s) => s.end(),
|
|
||||||
StructSerializers::MagicBuffer(s) => s.end(),
|
|
||||||
StructSerializers::Regular(s) => s.end(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serializes to JS Objects, NOT JS Maps ...
|
|
||||||
pub struct MapSerializer<'a, 'b, 'c> {
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
obj: v8::Local<'a, v8::Object>,
|
|
||||||
next_key: Option<JsValue<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> MapSerializer<'a, 'b, 'c> {
|
|
||||||
pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self {
|
|
||||||
let obj = v8::Object::new(&mut *scope.borrow_mut());
|
|
||||||
Self {
|
|
||||||
scope,
|
|
||||||
obj,
|
|
||||||
next_key: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::SerializeMap for MapSerializer<'a, 'b, 'c> {
|
|
||||||
type Ok = JsValue<'a>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<()> {
|
|
||||||
debug_assert!(self.next_key.is_none());
|
|
||||||
self.next_key = Some(key.serialize(Serializer::new(self.scope))?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_value<T: ?Sized + Serialize>(
|
|
||||||
&mut self,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<()> {
|
|
||||||
let v8_value = value.serialize(Serializer::new(self.scope))?;
|
|
||||||
let scope = &mut *self.scope.borrow_mut();
|
|
||||||
self.obj.set(scope, self.next_key.take().unwrap(), v8_value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
|
||||||
debug_assert!(self.next_key.is_none());
|
|
||||||
Ok(self.obj.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Serializer<'a, 'b, 'c> {
|
|
||||||
scope: ScopePtr<'a, 'b, 'c>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> Serializer<'a, 'b, 'c> {
|
|
||||||
pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self {
|
|
||||||
Serializer { scope }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! forward_to {
|
|
||||||
($($name:ident($ty:ty, $to:ident, $lt:lifetime);)*) => {
|
|
||||||
$(fn $name(self, v: $ty) -> JsResult<$lt> {
|
|
||||||
self.$to(v as _)
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
|
|
||||||
type Ok = v8::Local<'a, v8::Value>;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
type SerializeSeq = ArraySerializer<'a, 'b, 'c>;
|
|
||||||
type SerializeTuple = ArraySerializer<'a, 'b, 'c>;
|
|
||||||
type SerializeTupleStruct = ArraySerializer<'a, 'b, 'c>;
|
|
||||||
type SerializeTupleVariant =
|
|
||||||
VariantSerializer<'a, 'b, 'c, ArraySerializer<'a, 'b, 'c>>;
|
|
||||||
type SerializeMap = MapSerializer<'a, 'b, 'c>;
|
|
||||||
type SerializeStruct = StructSerializers<'a, 'b, 'c>;
|
|
||||||
type SerializeStructVariant =
|
|
||||||
VariantSerializer<'a, 'b, 'c, StructSerializers<'a, 'b, 'c>>;
|
|
||||||
|
|
||||||
forward_to! {
|
|
||||||
serialize_i8(i8, serialize_i32, 'a);
|
|
||||||
serialize_i16(i16, serialize_i32, 'a);
|
|
||||||
|
|
||||||
serialize_u8(u8, serialize_u32, 'a);
|
|
||||||
serialize_u16(u16, serialize_u32, 'a);
|
|
||||||
|
|
||||||
serialize_f32(f32, serialize_f64, 'a);
|
|
||||||
serialize_u64(u64, serialize_f64, 'a);
|
|
||||||
serialize_i64(i64, serialize_f64, 'a);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_i32(self, v: i32) -> JsResult<'a> {
|
|
||||||
Ok(v8::Integer::new(&mut self.scope.borrow_mut(), v).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_u32(self, v: u32) -> JsResult<'a> {
|
|
||||||
Ok(v8::Integer::new_from_unsigned(&mut self.scope.borrow_mut(), v).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_f64(self, v: f64) -> JsResult<'a> {
|
|
||||||
Ok(v8::Number::new(&mut self.scope.borrow_mut(), v).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_bool(self, v: bool) -> JsResult<'a> {
|
|
||||||
Ok(v8::Boolean::new(&mut self.scope.borrow_mut(), v).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_char(self, _v: char) -> JsResult<'a> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_str(self, v: &str) -> JsResult<'a> {
|
|
||||||
v8::String::new(&mut self.scope.borrow_mut(), v)
|
|
||||||
.map(|v| v.into())
|
|
||||||
.ok_or(Error::ExpectedString)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_bytes(self, _v: &[u8]) -> JsResult<'a> {
|
|
||||||
// TODO: investigate using Uint8Arrays
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_none(self) -> JsResult<'a> {
|
|
||||||
Ok(v8::null(&mut self.scope.borrow_mut()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> JsResult<'a> {
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit(self) -> JsResult<'a> {
|
|
||||||
Ok(v8::null(&mut self.scope.borrow_mut()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> JsResult<'a> {
|
|
||||||
Ok(v8::null(&mut self.scope.borrow_mut()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For compatibility with serde-json, serialises unit variants as "Variant" strings.
|
|
||||||
fn serialize_unit_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
) -> JsResult<'a> {
|
|
||||||
Ok(v8_struct_key(&mut self.scope.borrow_mut(), variant).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> JsResult<'a> {
|
|
||||||
value.serialize(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> JsResult<'a> {
|
|
||||||
let scope = self.scope;
|
|
||||||
let x = self.serialize_newtype_struct(variant, value)?;
|
|
||||||
VariantSerializer::new(scope, variant, x).end(Ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialises any Rust iterable into a JS Array
|
|
||||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
|
||||||
Ok(ArraySerializer::new(self.scope, len))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
|
|
||||||
self.serialize_seq(Some(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple_struct(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleStruct> {
|
|
||||||
self.serialize_tuple(len)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
len: usize,
|
|
||||||
) -> Result<Self::SerializeTupleVariant> {
|
|
||||||
Ok(VariantSerializer::new(
|
|
||||||
self.scope,
|
|
||||||
variant,
|
|
||||||
self.serialize_tuple_struct(variant, len)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
|
||||||
// Serializes a rust Map (e.g: BTreeMap, HashMap) to a v8 Object
|
|
||||||
// TODO: consider allowing serializing to v8 Maps (e.g: via a magic type)
|
|
||||||
// since they're lighter and better suited for K/V data
|
|
||||||
// and maybe restrict keys (e.g: strings and numbers)
|
|
||||||
Ok(MapSerializer::new(self.scope))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialises Rust typed structs into plain JS objects.
|
|
||||||
fn serialize_struct(
|
|
||||||
self,
|
|
||||||
name: &'static str,
|
|
||||||
_len: usize,
|
|
||||||
) -> Result<Self::SerializeStruct> {
|
|
||||||
match name {
|
|
||||||
magic::NAME => {
|
|
||||||
let m: MagicSerializer<'a> = MagicSerializer { v8_value: None };
|
|
||||||
Ok(StructSerializers::Magic(m))
|
|
||||||
}
|
|
||||||
magic::buffer::BUF_NAME => {
|
|
||||||
let m = MagicBufferSerializer::new(self.scope);
|
|
||||||
Ok(StructSerializers::MagicBuffer(m))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let o = ObjectSerializer::new(self.scope);
|
|
||||||
Ok(StructSerializers::Regular(o))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_struct_variant(
|
|
||||||
self,
|
|
||||||
_name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
len: usize,
|
|
||||||
) -> Result<Self::SerializeStructVariant> {
|
|
||||||
let scope = self.scope;
|
|
||||||
let x = self.serialize_struct(variant, len)?;
|
|
||||||
Ok(VariantSerializer::new(scope, variant, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to map MagicBuffers to v8
|
|
||||||
pub fn boxed_slice_to_uint8array<'a>(
|
|
||||||
scope: &mut v8::HandleScope<'a>,
|
|
||||||
buf: Box<[u8]>,
|
|
||||||
) -> v8::Local<'a, v8::Uint8Array> {
|
|
||||||
if buf.is_empty() {
|
|
||||||
let ab = v8::ArrayBuffer::new(scope, 0);
|
|
||||||
return v8::Uint8Array::new(scope, ab, 0, 0)
|
|
||||||
.expect("Failed to create UintArray8");
|
|
||||||
}
|
|
||||||
let buf_len = buf.len();
|
|
||||||
let backing_store = v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf);
|
|
||||||
let backing_store_shared = backing_store.make_shared();
|
|
||||||
let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared);
|
|
||||||
v8::Uint8Array::new(scope, ab, 0, buf_len)
|
|
||||||
.expect("Failed to create UintArray8")
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use std::any::TypeId;
|
|
||||||
use std::mem::transmute_copy;
|
|
||||||
|
|
||||||
/// Serializable exists to allow boxing values as "objects" to be serialized later,
|
|
||||||
/// this is particularly useful for async op-responses. This trait is a more efficient
|
|
||||||
/// replacement for erased-serde that makes less allocations, since it's specific to serde_v8
|
|
||||||
/// (and thus doesn't have to have generic outputs, etc...)
|
|
||||||
pub trait Serializable {
|
|
||||||
fn to_v8<'a>(
|
|
||||||
&self,
|
|
||||||
scope: &mut v8::HandleScope<'a>,
|
|
||||||
) -> Result<v8::Local<'a, v8::Value>, crate::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows all implementors of `serde::Serialize` to implement Serializable
|
|
||||||
impl<T: serde::Serialize> Serializable for T {
|
|
||||||
fn to_v8<'a>(
|
|
||||||
&self,
|
|
||||||
scope: &mut v8::HandleScope<'a>,
|
|
||||||
) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
|
|
||||||
crate::to_v8(scope, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SerializablePkg exists to provide a fast path for op returns,
|
|
||||||
/// allowing them to avoid boxing primtives (ints/floats/bool/unit/...)
|
|
||||||
pub enum SerializablePkg {
|
|
||||||
Primitive(Primitive),
|
|
||||||
Serializable(Box<dyn Serializable>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerializablePkg {
|
|
||||||
pub fn to_v8<'a>(
|
|
||||||
&self,
|
|
||||||
scope: &mut v8::HandleScope<'a>,
|
|
||||||
) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
|
|
||||||
match &*self {
|
|
||||||
Self::Primitive(x) => crate::to_v8(scope, x),
|
|
||||||
Self::Serializable(x) => x.to_v8(scope),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Primitive serves as a lightweight serializable wrapper around primitives
|
|
||||||
/// so that we can use them for async values
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Primitive {
|
|
||||||
Unit,
|
|
||||||
Bool(bool),
|
|
||||||
Int8(i8),
|
|
||||||
Int16(i16),
|
|
||||||
Int32(i32),
|
|
||||||
Int64(i64),
|
|
||||||
UInt8(u8),
|
|
||||||
UInt16(u16),
|
|
||||||
UInt32(u32),
|
|
||||||
UInt64(u64),
|
|
||||||
Float32(f32),
|
|
||||||
Float64(f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl serde::Serialize for Primitive {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
Self::Unit => serializer.serialize_unit(),
|
|
||||||
Self::Bool(x) => serializer.serialize_bool(x),
|
|
||||||
Self::Int8(x) => serializer.serialize_i8(x),
|
|
||||||
Self::Int16(x) => serializer.serialize_i16(x),
|
|
||||||
Self::Int32(x) => serializer.serialize_i32(x),
|
|
||||||
Self::Int64(x) => serializer.serialize_i64(x),
|
|
||||||
Self::UInt8(x) => serializer.serialize_u8(x),
|
|
||||||
Self::UInt16(x) => serializer.serialize_u16(x),
|
|
||||||
Self::UInt32(x) => serializer.serialize_u32(x),
|
|
||||||
Self::UInt64(x) => serializer.serialize_u64(x),
|
|
||||||
Self::Float32(x) => serializer.serialize_f32(x),
|
|
||||||
Self::Float64(x) => serializer.serialize_f64(x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: serde::Serialize + 'static> From<T> for SerializablePkg {
|
|
||||||
fn from(x: T) -> Self {
|
|
||||||
let tid = TypeId::of::<T>();
|
|
||||||
|
|
||||||
if tid == TypeId::of::<()>() {
|
|
||||||
Self::Primitive(Primitive::Unit)
|
|
||||||
} else if tid == TypeId::of::<bool>() {
|
|
||||||
Self::Primitive(Primitive::Bool(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<i8>() {
|
|
||||||
Self::Primitive(Primitive::Int8(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<i16>() {
|
|
||||||
Self::Primitive(Primitive::Int16(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<i32>() {
|
|
||||||
Self::Primitive(Primitive::Int32(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<i64>() {
|
|
||||||
Self::Primitive(Primitive::Int64(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<u8>() {
|
|
||||||
Self::Primitive(Primitive::UInt8(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<u16>() {
|
|
||||||
Self::Primitive(Primitive::UInt16(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<u32>() {
|
|
||||||
Self::Primitive(Primitive::UInt32(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<u64>() {
|
|
||||||
Self::Primitive(Primitive::UInt64(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<f32>() {
|
|
||||||
Self::Primitive(Primitive::Float32(unsafe { transmute_copy(&x) }))
|
|
||||||
} else if tid == TypeId::of::<f64>() {
|
|
||||||
Self::Primitive(Primitive::Float64(unsafe { transmute_copy(&x) }))
|
|
||||||
} else {
|
|
||||||
Self::Serializable(Box::new(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
pub fn js_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()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn v8_init() {
|
|
||||||
let platform = v8::new_default_platform().unwrap();
|
|
||||||
v8::V8::initialize_platform(platform);
|
|
||||||
v8::V8::initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn v8_shutdown() {
|
|
||||||
unsafe {
|
|
||||||
v8::V8::dispose();
|
|
||||||
}
|
|
||||||
v8::V8::shutdown_platform();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn v8_do(f: impl FnOnce()) {
|
|
||||||
static V8_INIT: Once = Once::new();
|
|
||||||
V8_INIT.call_once(|| {
|
|
||||||
v8_init();
|
|
||||||
});
|
|
||||||
f();
|
|
||||||
// v8_shutdown();
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use serde_v8::utils::{js_exec, v8_do};
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
|
||||||
struct MathOp {
|
|
||||||
pub a: u64,
|
|
||||||
pub b: u64,
|
|
||||||
pub operator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
|
||||||
enum EnumUnit {
|
|
||||||
A,
|
|
||||||
B,
|
|
||||||
C,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
|
||||||
enum EnumPayloads {
|
|
||||||
UInt(u64),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
Point { x: i64, y: i64 },
|
|
||||||
Tuple(bool, i64, ()),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dedo(
|
|
||||||
code: &str,
|
|
||||||
f: impl FnOnce(&mut v8::HandleScope, v8::Local<v8::Value>),
|
|
||||||
) {
|
|
||||||
v8_do(|| {
|
|
||||||
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);
|
|
||||||
let v = js_exec(scope, code);
|
|
||||||
|
|
||||||
f(scope, v);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! detest {
|
|
||||||
($fn_name:ident, $t:ty, $src:expr, $rust:expr) => {
|
|
||||||
#[test]
|
|
||||||
fn $fn_name() {
|
|
||||||
dedo($src, |scope, v| {
|
|
||||||
let rt = serde_v8::from_v8(scope, v);
|
|
||||||
assert!(rt.is_ok(), "from_v8(\"{}\"): {:?}", $src, rt.err());
|
|
||||||
let t: $t = rt.unwrap();
|
|
||||||
assert_eq!(t, $rust);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
detest!(de_option_some, Option<bool>, "true", Some(true));
|
|
||||||
detest!(de_option_null, Option<bool>, "null", None);
|
|
||||||
detest!(de_option_undefined, Option<bool>, "undefined", None);
|
|
||||||
detest!(de_unit_null, (), "null", ());
|
|
||||||
detest!(de_unit_undefined, (), "undefined", ());
|
|
||||||
detest!(de_bool, bool, "true", true);
|
|
||||||
detest!(de_u64, u64, "32", 32);
|
|
||||||
detest!(de_string, String, "'Hello'", "Hello".to_owned());
|
|
||||||
detest!(de_vec_u64, Vec<u64>, "[1,2,3,4,5]", vec![1, 2, 3, 4, 5]);
|
|
||||||
detest!(
|
|
||||||
de_vec_str,
|
|
||||||
Vec<String>,
|
|
||||||
"['hello', 'world']",
|
|
||||||
vec!["hello".to_owned(), "world".to_owned()]
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_tuple,
|
|
||||||
(u64, bool, ()),
|
|
||||||
"[123, true, null]",
|
|
||||||
(123, true, ())
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_mathop,
|
|
||||||
MathOp,
|
|
||||||
"({a: 1, b: 3, c: 'ignored'})",
|
|
||||||
MathOp {
|
|
||||||
a: 1,
|
|
||||||
b: 3,
|
|
||||||
operator: None
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Unit enums
|
|
||||||
detest!(de_enum_unit_a, EnumUnit, "'A'", EnumUnit::A);
|
|
||||||
detest!(de_enum_unit_b, EnumUnit, "'B'", EnumUnit::B);
|
|
||||||
detest!(de_enum_unit_c, EnumUnit, "'C'", EnumUnit::C);
|
|
||||||
|
|
||||||
// Enums with payloads (tuples & struct)
|
|
||||||
detest!(
|
|
||||||
de_enum_payload_int,
|
|
||||||
EnumPayloads,
|
|
||||||
"({ Int: -123 })",
|
|
||||||
EnumPayloads::Int(-123)
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_enum_payload_uint,
|
|
||||||
EnumPayloads,
|
|
||||||
"({ UInt: 123 })",
|
|
||||||
EnumPayloads::UInt(123)
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_enum_payload_float,
|
|
||||||
EnumPayloads,
|
|
||||||
"({ Float: 1.23 })",
|
|
||||||
EnumPayloads::Float(1.23)
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_enum_payload_point,
|
|
||||||
EnumPayloads,
|
|
||||||
"({ Point: { x: 1, y: 2 } })",
|
|
||||||
EnumPayloads::Point { x: 1, y: 2 }
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_enum_payload_tuple,
|
|
||||||
EnumPayloads,
|
|
||||||
"({ Tuple: [true, 123, null ] })",
|
|
||||||
EnumPayloads::Tuple(true, 123, ())
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn de_f64() {
|
|
||||||
dedo("12345.0", |scope, v| {
|
|
||||||
let x: f64 = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
assert!((x - 12345.0).abs() < f64::EPSILON);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn de_map() {
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
dedo("({a: 1, b: 2, c: 3})", |scope, v| {
|
|
||||||
let map: HashMap<String, u64> = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
assert_eq!(map.get("a").cloned(), Some(1));
|
|
||||||
assert_eq!(map.get("b").cloned(), Some(2));
|
|
||||||
assert_eq!(map.get("c").cloned(), Some(3));
|
|
||||||
assert_eq!(map.get("nada"), None);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
////
|
|
||||||
// JSON tests: serde_json::Value compatibility
|
|
||||||
////
|
|
||||||
|
|
||||||
detest!(
|
|
||||||
de_json_null,
|
|
||||||
serde_json::Value,
|
|
||||||
"null",
|
|
||||||
serde_json::Value::Null
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_bool,
|
|
||||||
serde_json::Value,
|
|
||||||
"true",
|
|
||||||
serde_json::Value::Bool(true)
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_int,
|
|
||||||
serde_json::Value,
|
|
||||||
"123",
|
|
||||||
serde_json::Value::Number(serde_json::Number::from(123))
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_float,
|
|
||||||
serde_json::Value,
|
|
||||||
"123.45",
|
|
||||||
serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap())
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_string,
|
|
||||||
serde_json::Value,
|
|
||||||
"'Hello'",
|
|
||||||
serde_json::Value::String("Hello".to_string())
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_vec_string,
|
|
||||||
serde_json::Value,
|
|
||||||
"['Hello', 'World']",
|
|
||||||
serde_json::Value::Array(vec![
|
|
||||||
serde_json::Value::String("Hello".to_string()),
|
|
||||||
serde_json::Value::String("World".to_string())
|
|
||||||
])
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_tuple,
|
|
||||||
serde_json::Value,
|
|
||||||
"[true, 'World', 123.45, null]",
|
|
||||||
serde_json::Value::Array(vec![
|
|
||||||
serde_json::Value::Bool(true),
|
|
||||||
serde_json::Value::String("World".to_string()),
|
|
||||||
serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap()),
|
|
||||||
serde_json::Value::Null,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
detest!(
|
|
||||||
de_json_object,
|
|
||||||
serde_json::Value,
|
|
||||||
"({a: 1, b: 'hello', c: true})",
|
|
||||||
serde_json::Value::Object(
|
|
||||||
vec![
|
|
||||||
(
|
|
||||||
"a".to_string(),
|
|
||||||
serde_json::Value::Number(serde_json::Number::from(1)),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"b".to_string(),
|
|
||||||
serde_json::Value::String("hello".to_string()),
|
|
||||||
),
|
|
||||||
("c".to_string(), serde_json::Value::Bool(true),),
|
|
||||||
]
|
|
||||||
.drain(..)
|
|
||||||
.collect()
|
|
||||||
)
|
|
||||||
);
|
|
|
@ -1,124 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use serde_v8::utils::{js_exec, v8_do};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct MagicOp<'s> {
|
|
||||||
pub a: u64,
|
|
||||||
pub b: u64,
|
|
||||||
pub c: serde_v8::Value<'s>,
|
|
||||||
pub operator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct MagicContainer<'s> {
|
|
||||||
pub magic: bool,
|
|
||||||
pub contains: serde_v8::Value<'s>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn magic_basic() {
|
|
||||||
v8_do(|| {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Decode
|
|
||||||
let v = js_exec(scope, "({a: 1, b: 3, c: 'abracadabra'})");
|
|
||||||
let mop: MagicOp = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
// Check string
|
|
||||||
let v8_value: v8::Local<v8::Value> = mop.c.into();
|
|
||||||
let vs = v8::Local::<v8::String>::try_from(v8_value).unwrap();
|
|
||||||
let s = vs.to_rust_string_lossy(scope);
|
|
||||||
assert_eq!(s, "abracadabra");
|
|
||||||
|
|
||||||
// Encode
|
|
||||||
let container = MagicContainer {
|
|
||||||
magic: true,
|
|
||||||
contains: v.into(),
|
|
||||||
};
|
|
||||||
let vc = serde_v8::to_v8(scope, container).unwrap();
|
|
||||||
// JSON stringify & check
|
|
||||||
let json = v8::json::stringify(scope, vc).unwrap();
|
|
||||||
let s2 = json.to_rust_string_lossy(scope);
|
|
||||||
assert_eq!(
|
|
||||||
s2,
|
|
||||||
r#"{"magic":true,"contains":{"a":1,"b":3,"c":"abracadabra"}}"#
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn magic_buffer() {
|
|
||||||
v8_do(|| {
|
|
||||||
// Init isolate
|
|
||||||
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);
|
|
||||||
let global = context.global(scope);
|
|
||||||
|
|
||||||
// Simple buffer
|
|
||||||
let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])");
|
|
||||||
let zbuf: serde_v8::Buffer = serde_v8::from_v8(scope, v8_array).unwrap();
|
|
||||||
assert_eq!(&*zbuf, &[1, 2, 3, 4, 5]);
|
|
||||||
|
|
||||||
// Multi buffers
|
|
||||||
let v8_arrays =
|
|
||||||
js_exec(scope, "[new Uint8Array([1,2]), new Uint8Array([3,4,5])]");
|
|
||||||
let (z1, z2): (serde_v8::Buffer, serde_v8::Buffer) =
|
|
||||||
serde_v8::from_v8(scope, v8_arrays).unwrap();
|
|
||||||
assert_eq!(&*z1, &[1, 2]);
|
|
||||||
assert_eq!(&*z2, &[3, 4, 5]);
|
|
||||||
|
|
||||||
// Wrapped in option, like our current op-ABI
|
|
||||||
let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])");
|
|
||||||
let zbuf: Option<serde_v8::Buffer> =
|
|
||||||
serde_v8::from_v8(scope, v8_array).unwrap();
|
|
||||||
assert_eq!(&*zbuf.unwrap(), &[1, 2, 3, 4, 5]);
|
|
||||||
|
|
||||||
// Observe mutation in JS
|
|
||||||
let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])");
|
|
||||||
let mut zbuf: serde_v8::Buffer =
|
|
||||||
serde_v8::from_v8(scope, v8_array).unwrap();
|
|
||||||
let key = serde_v8::to_v8(scope, "t1").unwrap();
|
|
||||||
global.set(scope, key, v8_array);
|
|
||||||
(&mut *zbuf)[2] = 42;
|
|
||||||
let eq = js_exec(scope, "t1[2] === 42");
|
|
||||||
assert!(eq.is_true());
|
|
||||||
|
|
||||||
// Serialization
|
|
||||||
let buf: Vec<u8> = vec![1, 2, 3, 99, 5];
|
|
||||||
let zbuf: serde_v8::Buffer = buf.into();
|
|
||||||
let v8_value = serde_v8::to_v8(scope, zbuf).unwrap();
|
|
||||||
let key = serde_v8::to_v8(scope, "t2").unwrap();
|
|
||||||
global.set(scope, key, v8_value);
|
|
||||||
let eq = js_exec(scope, "t2[3] === 99");
|
|
||||||
assert!(eq.is_true());
|
|
||||||
|
|
||||||
// Composite Serialization
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
struct Wrapper {
|
|
||||||
a: serde_v8::Buffer,
|
|
||||||
b: serde_v8::Buffer,
|
|
||||||
}
|
|
||||||
let buf1: Vec<u8> = vec![1, 2, 33, 4, 5];
|
|
||||||
let buf2: Vec<u8> = vec![5, 4, 3, 2, 11];
|
|
||||||
let wrapped = Wrapper {
|
|
||||||
a: buf1.into(),
|
|
||||||
b: buf2.into(),
|
|
||||||
};
|
|
||||||
let v8_value = serde_v8::to_v8(scope, wrapped).unwrap();
|
|
||||||
let key = serde_v8::to_v8(scope, "t3").unwrap();
|
|
||||||
global.set(scope, key, v8_value);
|
|
||||||
let eq = js_exec(scope, "t3.a[2] === 33");
|
|
||||||
assert!(eq.is_true());
|
|
||||||
let eq = js_exec(scope, "t3.b[4] === 11");
|
|
||||||
assert!(eq.is_true());
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
use rusty_v8 as v8;
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::json;
|
|
||||||
use serde_v8::utils::{js_exec, v8_do};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, PartialEq)]
|
|
||||||
struct MathOp {
|
|
||||||
pub a: u64,
|
|
||||||
pub b: u64,
|
|
||||||
pub operator: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility JS code (obj equality, etc...)
|
|
||||||
const JS_UTILS: &str = r#"
|
|
||||||
// Shallow obj equality (don't use deep objs for now)
|
|
||||||
function objEqual(a, b) {
|
|
||||||
const ka = Object.keys(a);
|
|
||||||
const kb = Object.keys(b);
|
|
||||||
return ka.length === kb.length && ka.every(k => a[k] === b[k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrEqual(a, b) {
|
|
||||||
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
fn sercheck<T: Serialize>(val: T, code: &str) -> bool {
|
|
||||||
let mut equal = false;
|
|
||||||
|
|
||||||
v8_do(|| {
|
|
||||||
// Setup isolate
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Set value as "x" in global scope
|
|
||||||
let global = context.global(scope);
|
|
||||||
let v8_key = serde_v8::to_v8(scope, "x").unwrap();
|
|
||||||
let v8_val = serde_v8::to_v8(scope, val).unwrap();
|
|
||||||
global.set(scope, v8_key, v8_val);
|
|
||||||
|
|
||||||
// Load util functions
|
|
||||||
js_exec(scope, JS_UTILS);
|
|
||||||
// Execute equality check in JS (e.g: x == ...)
|
|
||||||
let v = js_exec(scope, code);
|
|
||||||
// Cast to bool
|
|
||||||
equal = serde_v8::from_v8(scope, v).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
equal
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! sertest {
|
|
||||||
($fn_name:ident, $rust:expr, $src:expr) => {
|
|
||||||
#[test]
|
|
||||||
fn $fn_name() {
|
|
||||||
assert!(
|
|
||||||
sercheck($rust, $src),
|
|
||||||
"Expected: {} where x={:?}",
|
|
||||||
$src,
|
|
||||||
$rust,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
sertest!(ser_option_some, Some(true), "x === true");
|
|
||||||
sertest!(ser_option_null, None as Option<bool>, "x === null");
|
|
||||||
sertest!(ser_unit_null, (), "x === null");
|
|
||||||
sertest!(ser_bool, true, "x === true");
|
|
||||||
sertest!(ser_u64, 32, "x === 32");
|
|
||||||
sertest!(ser_f64, 12345.0, "x === 12345.0");
|
|
||||||
sertest!(ser_string, "Hello".to_owned(), "x === 'Hello'");
|
|
||||||
sertest!(ser_vec_u64, vec![1, 2, 3, 4, 5], "arrEqual(x, [1,2,3,4,5])");
|
|
||||||
sertest!(
|
|
||||||
ser_vec_string,
|
|
||||||
vec!["hello".to_owned(), "world".to_owned(),],
|
|
||||||
"arrEqual(x, ['hello', 'world'])"
|
|
||||||
);
|
|
||||||
sertest!(ser_tuple, (123, true, ()), "arrEqual(x, [123, true, null])");
|
|
||||||
sertest!(
|
|
||||||
ser_mathop,
|
|
||||||
MathOp {
|
|
||||||
a: 1,
|
|
||||||
b: 3,
|
|
||||||
operator: None
|
|
||||||
},
|
|
||||||
"objEqual(x, {a: 1, b: 3, operator: null})"
|
|
||||||
);
|
|
||||||
|
|
||||||
sertest!(
|
|
||||||
ser_map,
|
|
||||||
{
|
|
||||||
let map: std::collections::BTreeMap<&str, u32> =
|
|
||||||
vec![("a", 1), ("b", 2), ("c", 3)].drain(..).collect();
|
|
||||||
map
|
|
||||||
},
|
|
||||||
"objEqual(x, {a: 1, b: 2, c: 3})"
|
|
||||||
);
|
|
||||||
|
|
||||||
////
|
|
||||||
// JSON tests: json!() compatibility
|
|
||||||
////
|
|
||||||
sertest!(ser_json_bool, json!(true), "x === true");
|
|
||||||
sertest!(ser_json_null, json!(null), "x === null");
|
|
||||||
sertest!(ser_json_int, json!(123), "x === 123");
|
|
||||||
sertest!(ser_json_f64, json!(123.45), "x === 123.45");
|
|
||||||
sertest!(ser_json_string, json!("Hello World"), "x === 'Hello World'");
|
|
||||||
sertest!(ser_json_obj_empty, json!({}), "objEqual(x, {})");
|
|
||||||
sertest!(
|
|
||||||
ser_json_obj,
|
|
||||||
json!({"a": 1, "b": 2, "c": true}),
|
|
||||||
"objEqual(x, {a: 1, b: 2, c: true})"
|
|
||||||
);
|
|
||||||
sertest!(
|
|
||||||
ser_json_vec_int,
|
|
||||||
json!([1, 2, 3, 4, 5]),
|
|
||||||
"arrEqual(x, [1,2,3,4,5])"
|
|
||||||
);
|
|
||||||
sertest!(
|
|
||||||
ser_json_vec_string,
|
|
||||||
json!(["Goodbye", "Dinosaurs 👋☄️"]),
|
|
||||||
"arrEqual(x, ['Goodbye', 'Dinosaurs 👋☄️'])"
|
|
||||||
);
|
|
||||||
sertest!(
|
|
||||||
ser_json_tuple,
|
|
||||||
json!([true, 42, "nabla"]),
|
|
||||||
"arrEqual(x, [true, 42, 'nabla'])"
|
|
||||||
);
|
|
|
@ -30,9 +30,6 @@ publish those crates.**
|
||||||
This is done by running `cargo publish` in each crate, because of dependencies
|
This is done by running `cargo publish` in each crate, because of dependencies
|
||||||
between the crates, it must be done in specific order:
|
between the crates, it must be done in specific order:
|
||||||
|
|
||||||
- `serde_v8` - `deno_core` depends on it, but this crate shouldn't change that
|
|
||||||
often, so you might want to skip publishing a new version if there are no
|
|
||||||
changes
|
|
||||||
- `deno_core` - all crates depend on `deno_core` so it must always be published
|
- `deno_core` - all crates depend on `deno_core` so it must always be published
|
||||||
first
|
first
|
||||||
- `bench_util`
|
- `bench_util`
|
||||||
|
|
Loading…
Reference in a new issue